Compare commits

...
109 Commits
Author SHA1 Message Date
Cody Thomas 96b24b48a7 Merge pull request #594 from je4yfc/integration
Generalized Support for external services across compose generation and port checks
2026-10-11 10:24:27 -05:00
Cody Thomas bdc722f6ce Merge pull request #589 from mjmaley/fix/cli-pasted-whitespace
Handle pasted whitespace in task arguments
2026-10-08 10:39:11 -05:00
Joseph Erdosy f9e71d0859 feat(cli): generalize external service handling in compose configuration and port checks
Signed-off-by: Joseph Erdosy <[email protected]>
2026-09-10 10:46:37 -04:00
Joseph Erdosy 742d878928 fix(cli): support external Nginx and create config dir for block lists
Signed-off-by: Joseph Erdosy <[email protected]>
2026-09-10 10:46:34 -04:00
Joseph Erdosy 01ea8bc896 feat(database): add external PostgreSQL support and configurable SSL mode (#587, #590)
Signed-off-by: Joseph Erdosy <[email protected]>
2026-09-10 10:46:31 -04:00
pwn 94418bcd52 fix(cli): fail fast on docker-compose failures instead of misleading health loop
The pty path in runDockerCompose returned the (nil) error variable on
nonzero exit, and start() discarded ServiceStart's error. Both together
let 'mythic-cli start' proceed into the UI probe loop after a failed
compose up, ending in os.Exit(1) with unrelated diagnostics (run-1.log).

- dockerComposeManager.runDockerCompose: return a descriptive error on
  nonzero exit in the pty path
- start: log.Fatalf on ServiceStart error so compose failures surface
  immediately

Signed-off-by: Joseph Erdosy <[email protected]>
2026-09-10 10:46:28 -04:00
Cody Thomas 3ae9c5fe18 Merge pull request #593 from je4yfc/cli-error-handling-fixes
fix(cli): fail fast on docker-compose failures in mythic-cli start
2026-09-07 10:57:19 -05:00
pwn 5e85a20d14 fix(cli): fail fast on docker-compose failures instead of misleading health loop
The pty path in runDockerCompose returned the (nil) error variable on
nonzero exit, and start() discarded ServiceStart's error. Both together
let 'mythic-cli start' proceed into the UI probe loop after a failed
compose up, ending in os.Exit(1) with unrelated diagnostics (run-1.log).

- dockerComposeManager.runDockerCompose: return a descriptive error on
  nonzero exit in the pty path
- start: log.Fatalf on ServiceStart error so compose failures surface
  immediately
2026-09-06 14:29:33 -04:00
mjmaley 5f46db6ede Handle pasted whitespace in task arguments 2026-09-02 16:56:36 -04:00
its-a-feature e37e057a89 process browser fix 2026-07-16 13:02:35 -07:00
github-actions 33a46d7db7 Bump Mythic Dockerfile tag to match release 'v3.4.0.61' 2026-07-01 16:06:33 +00:00
its-a-feature 540aec0f00 fixing filtering bug in process browser 2026-07-01 08:45:06 -07:00
github-actions 29be09eef4 Bump Mythic Dockerfile tag to match release 'v3.4.0.60' 2026-06-21 17:04:09 +00:00
its-a-feature a5a1a972d7 updating versioning 2026-06-21 09:09:15 -07:00
its-a-feature 82648e8241 addressing issues #563 #564 and #565 2026-06-21 09:04:45 -07:00
github-actions e4da2c887f Bump Mythic Dockerfile tag to match release 'v3.4.0.59' 2026-06-04 18:25:04 +00:00
Cody Thomas 39f44f7dec Merge pull request #557 from anuj-modi/internal-registry-support
Internal Registry Support
2026-06-04 12:54:16 -05:00
Anuj Modi 0f51584493 add some logs so users know Mirror is configured 2026-05-27 10:35:40 -04:00
Anuj Modi dc50ecc0dc refactor to one variable rather than 2 2026-05-26 16:06:20 -04:00
Anuj Modi 59211a2fbf Add support for Interal Registries for Core images and Agents 2026-05-21 15:46:53 -04:00
its-a-feature f8d355a29c updating single task view 2026-05-07 16:16:32 -05:00
github-actions 1da8fdcd2a Bump Mythic Dockerfile tag to match release 'v3.4.0.58' 2026-05-07 20:08:16 +00:00
its-a-feature 2dcc3b5699 fixing lazyQuery on single task view page 2026-05-07 14:47:22 -05:00
Cody Thomas 8deec59270 Merge pull request #548 from s1nc3ju1y/fix-jupyter-port-env
Pass JUPYTER_PORT into mythic_jupyter container
2026-04-28 13:58:22 -05:00
s1nc3ju1y 071abeaf74 Pass JUPYTER_PORT into mythic_jupyter container 2026-04-28 19:32:05 +08:00
its-a-feature 6eb53da6b5 slight UI updates, additional color choice added, some formatting 2026-04-16 17:39:11 -05:00
github-actions d605a2f82c Bump Mythic Dockerfile tag to match release 'v3.4.0.57' 2026-04-16 13:28:59 +00:00
its-a-feature 31db999945 updating version and readme 2026-04-16 08:07:33 -05:00
Cody Thomas 9ae193be76 Merge pull request #546 from Kargha/fix-missing-return-apitokengen
fix missing return in GenerateAPITokenWebhook
2026-04-16 07:59:01 -05:00
Andreas Persson fc731af819 fix missing return in GenerateAPITokenWebhook 2026-04-16 12:17:38 +09:00
Cody Thomas d21dbf293b Merge pull request #541 from its-a-feature/dependabot/go_modules/Mythic_CLI/src/go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp-1.43.0
Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp from 1.25.0 to 1.43.0 in /Mythic_CLI/src
2026-04-15 16:13:55 -05:00
dependabot[bot] b6d98b1794 Bump go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
Bumps [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp](https://github.com/open-telemetry/opentelemetry-go) from 1.25.0 to 1.43.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.25.0...v1.43.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
  dependency-version: 1.43.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-15 21:11:32 +00:00
Cody Thomas 448a57b82b Merge pull request #542 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/lodash-4.18.1
Bump lodash from 4.17.23 to 4.18.1 in /MythicReactUI
2026-04-15 16:10:35 -05:00
Cody Thomas 1d54d5ea0c Merge pull request #543 from its-a-feature/dependabot/go_modules/Mythic_CLI/src/go.opentelemetry.io/otel/sdk-1.43.0
Bump go.opentelemetry.io/otel/sdk from 1.42.0 to 1.43.0 in /Mythic_CLI/src
2026-04-15 16:10:14 -05:00
Cody Thomas 4d793d5998 Merge pull request #544 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/follow-redirects-1.16.0
Bump follow-redirects from 1.15.9 to 1.16.0 in /MythicReactUI
2026-04-15 16:09:29 -05:00
its-a-feature 60455c26cb add mythic server name to emitted log messages 2026-04-15 08:34:52 -05:00
Cody Thomas 183f0605c2 Merge pull request #545 from Kargha/fix-emit-task-log-operation-id
Fix OperationID always being 0 in emitTaskLog
2026-04-15 08:20:36 -05:00
Andreas Persson 37d3cc8b1b Fix OperationID always being 0 in emitTaskLog 2026-04-15 21:26:48 +09:00
dependabot[bot] 9532b96f7a Bump follow-redirects from 1.15.9 to 1.16.0 in /MythicReactUI
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.15.9 to 1.16.0.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.15.9...v1.16.0)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-version: 1.16.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-15 11:12:15 +00:00
its-a-feature e1806f0fd5 updating event log message handling for performance updates 2026-04-13 09:08:13 -05:00
github-actions fa235b6378 Bump Mythic Dockerfile tag to match release 'v3.4.0.56' 2026-04-10 15:36:04 +00:00
its-a-feature b87219b1e9 updating c2 profile build to leverage ui_position specifications 2026-04-10 10:14:05 -05:00
github-actions b5459b56c8 Bump Mythic Dockerfile tag to match release 'v3.4.0.55' 2026-04-10 14:57:33 +00:00
its-a-feature ca6d7d2ce0 fixed an issue with database connection exhaustion on errors 2026-04-10 09:35:59 -05:00
github-actions 6148c5f0fb Bump Mythic Dockerfile tag to match release 'v3.4.0.54' 2026-04-10 13:42:54 +00:00
its-a-feature 6e14b2a472 updating postgres healthcheck 2026-04-10 08:21:55 -05:00
dependabot[bot] 307df57df8 Bump go.opentelemetry.io/otel/sdk in /Mythic_CLI/src
Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.42.0 to 1.43.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.42.0...v1.43.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.43.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-08 20:27:11 +00:00
dependabot[bot] 4a4d95f1ca Bump lodash from 4.17.23 to 4.18.1 in /MythicReactUI
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.23 to 4.18.1.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.23...4.18.1)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.18.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-04-08 20:23:39 +00:00
github-actions ffc5041349 Bump Mythic Dockerfile tag to match release 'v3.4.0.53' 2026-04-06 21:49:43 +00:00
its-a-feature ecda245963 updating version tracking for merged PR 2026-04-06 16:27:36 -05:00
Cody Thomas 6ea67c69b9 Merge pull request #539 from nt0xa/auth_acs-get-handler
GET request handler for /auth_acs/:containerName/:IDPName
2026-04-06 16:25:10 -05:00
Anton Prokhorov 64d32eb116 GET request handler for /auth_acs/:containerName/:IDPName 2026-04-06 22:11:22 +01:00
Cody Thomas 9f7a1ce770 Merge pull request #537 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/node-forge-1.4.0
Bump node-forge from 1.3.2 to 1.4.0 in /MythicReactUI
2026-03-27 13:40:12 -05:00
dependabot[bot] 4a3818988c Bump node-forge from 1.3.2 to 1.4.0 in /MythicReactUI
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.2 to 1.4.0.
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.2...v1.4.0)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-version: 1.4.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-27 18:39:09 +00:00
Cody Thomas 1ec6318b62 Merge pull request #536 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/picomatch-2.3.2
Bump picomatch from 2.3.1 to 2.3.2 in /MythicReactUI
2026-03-27 13:38:39 -05:00
Cody Thomas 60902a6e66 Merge pull request #535 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/multi-c136cad177
Bump yaml in /MythicReactUI
2026-03-27 13:38:11 -05:00
dependabot[bot] ddda1a266c Bump picomatch from 2.3.1 to 2.3.2 in /MythicReactUI
Bumps [picomatch](https://github.com/micromatch/picomatch) from 2.3.1 to 2.3.2.
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-25 22:25:24 +00:00
dependabot[bot] 8dc9f351d9 Bump yaml in /MythicReactUI
Bumps  and [yaml](https://github.com/eemeli/yaml). These dependencies needed to be updated together.

Updates `yaml` from 1.10.2 to 1.10.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

Updates `yaml` from 2.7.1 to 2.8.3
- [Release notes](https://github.com/eemeli/yaml/releases)
- [Commits](https://github.com/eemeli/yaml/compare/v1.10.2...v1.10.3)

---
updated-dependencies:
- dependency-name: yaml
  dependency-version: 1.10.3
  dependency-type: indirect
- dependency-name: yaml
  dependency-version: 2.8.3
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-25 20:21:11 +00:00
github-actions d7b4d51186 Bump Mythic Dockerfile tag to match release 'v3.4.0.52' 2026-03-24 13:42:04 +00:00
its-a-feature d2334b79de bumping dependencies 2026-03-24 08:20:20 -05:00
Cody Thomas 96df4db36e Merge pull request #534 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/jsonpath-1.3.0
Bump jsonpath from 1.2.1 to 1.3.0 in /MythicReactUI
2026-03-24 08:09:35 -05:00
dependabot[bot] a72dd1b086 Bump jsonpath from 1.2.1 to 1.3.0 in /MythicReactUI
Bumps [jsonpath](https://github.com/dchester/jsonpath) from 1.2.1 to 1.3.0.
- [Commits](https://github.com/dchester/jsonpath/commits)

---
updated-dependencies:
- dependency-name: jsonpath
  dependency-version: 1.3.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-24 13:08:48 +00:00
Cody Thomas 89bee21894 Merge pull request #529 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/flatted-3.4.2
Bump flatted from 3.3.3 to 3.4.2 in /MythicReactUI
2026-03-24 08:08:11 -05:00
Cody Thomas 26a4410f0a Merge pull request #527 from its-a-feature/dependabot/go_modules/mythic-docker/src/google.golang.org/grpc-1.79.3
Bump google.golang.org/grpc from 1.79.2 to 1.79.3 in /mythic-docker/src
2026-03-24 08:07:51 -05:00
github-actions 36fb4c58bf Bump Mythic Dockerfile tag to match release 'v3.4.0.51' 2026-03-23 15:30:55 +00:00
its-a-feature cfdf0c537d Merge remote-tracking branch 'origin/master' 2026-03-23 10:09:04 -05:00
its-a-feature f23e123a56 fixing file browser bug, upload bug, and file removal bug 2026-03-23 10:08:57 -05:00
github-actions 5b4c210e6f Bump Mythic Dockerfile tag to match release 'v3.4.0.50' 2026-03-23 14:06:16 +00:00
its-a-feature 3dee8797da updating to fix file click issue 2026-03-21 14:53:40 -05:00
dependabot[bot] 33f48baab4 Bump flatted from 3.3.3 to 3.4.2 in /MythicReactUI
Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2.
- [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-21 10:30:41 +00:00
github-actions 3198f304c3 Bump Mythic Dockerfile tag to match release 'v3.4.0.49' 2026-03-20 23:03:14 +00:00
its-a-feature 09ac733d45 UI updates and back-end better file transfer tracking 2026-03-20 17:27:25 -05:00
its-a-feature 3e611494cc First set of draggable modals to access content behind them 2026-03-19 19:05:33 -05:00
dependabot[bot] 8be3c084e5 Bump google.golang.org/grpc from 1.79.2 to 1.79.3 in /mythic-docker/src
Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.79.2 to 1.79.3.
- [Release notes](https://github.com/grpc/grpc-go/releases)
- [Commits](https://github.com/grpc/grpc-go/compare/v1.79.2...v1.79.3)

---
updated-dependencies:
- dependency-name: google.golang.org/grpc
  dependency-version: 1.79.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-19 02:35:13 +00:00
github-actions 3796640835 Bump Mythic Dockerfile tag to match release 'v3.4.0.48' 2026-03-18 20:17:23 +00:00
its-a-feature 6e23b29f22 tweaking how MythicRPCFileSearch works to prevent duplicates 2026-03-18 14:55:38 -05:00
github-actions 22e5c489b2 Bump Mythic Dockerfile tag to match release 'v3.4.0.47' 2026-03-17 17:18:52 +00:00
its-a-feature 8cdf34c3e7 fixing bug with file uploads in recreated build parameters 2026-03-17 11:22:41 -05:00
its-a-feature b285558fce fixing bug with nil path for screenshots 2026-03-17 08:26:34 -05:00
its-a-feature e536f59347 updating mythic-cli go docker version 2026-03-17 07:57:54 -05:00
its-a-feature 4c472f7e3a updating go modules for mythic_server and mythic-cli 2026-03-16 14:31:34 -05:00
Cody Thomas cc77f2b1a3 Merge pull request #525 from its-a-feature/dependabot/go_modules/Mythic_CLI/src/golang.org/x/net-0.38.0
Bump golang.org/x/net from 0.28.0 to 0.38.0 in /Mythic_CLI/src
2026-03-16 09:58:23 -05:00
dependabot[bot] 1c18703e96 Bump golang.org/x/net from 0.28.0 to 0.38.0 in /Mythic_CLI/src
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.28.0 to 0.38.0.
- [Commits](https://github.com/golang/net/compare/v0.28.0...v0.38.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.38.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-16 14:57:34 +00:00
Cody Thomas f017661b3d Merge pull request #520 from its-a-feature/dependabot/go_modules/Mythic_CLI/src/go.opentelemetry.io/otel/sdk-1.40.0
Bump go.opentelemetry.io/otel/sdk from 1.25.0 to 1.40.0 in /Mythic_CLI/src
2026-03-16 09:56:49 -05:00
Cody Thomas 98dd30d984 Merge pull request #501 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/multi-75e6bc5210
Bump js-yaml in /MythicReactUI
2026-03-16 09:55:30 -05:00
Cody Thomas eb3cf1053b Merge pull request #524 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/multi-b5ddb13e79
Bump react-router and react-router-dom in /MythicReactUI
2026-03-16 09:54:19 -05:00
Cody Thomas 58e2c6de5f Merge pull request #522 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/ajv-6.14.0
Bump ajv from 6.12.6 to 6.14.0 in /MythicReactUI
2026-03-16 09:53:17 -05:00
Cody Thomas 7dc66c43aa Merge pull request #521 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/rollup-2.80.0
Bump rollup from 2.79.2 to 2.80.0 in /MythicReactUI
2026-03-16 09:52:53 -05:00
dependabot[bot] 9c6afe2d8c Bump react-router and react-router-dom in /MythicReactUI
Bumps [react-router](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router) to 7.13.1 and updates ancestor dependency [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom). These dependencies need to be updated together.


Updates `react-router` from 7.5.2 to 7.13.1
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/[email protected]/packages/react-router)

Updates `react-router-dom` from 7.5.2 to 7.13.1
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/[email protected]/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router
  dependency-version: 7.13.1
  dependency-type: indirect
- dependency-name: react-router-dom
  dependency-version: 7.13.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-16 14:50:21 +00:00
dependabot[bot] 6c5b1d2a24 Bump ajv from 6.12.6 to 6.14.0 in /MythicReactUI
Bumps [ajv](https://github.com/ajv-validator/ajv) from 6.12.6 to 6.14.0.
- [Release notes](https://github.com/ajv-validator/ajv/releases)
- [Commits](https://github.com/ajv-validator/ajv/compare/v6.12.6...v6.14.0)

---
updated-dependencies:
- dependency-name: ajv
  dependency-version: 6.14.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-16 14:50:01 +00:00
dependabot[bot] fc8239eb85 Bump rollup from 2.79.2 to 2.80.0 in /MythicReactUI
Bumps [rollup](https://github.com/rollup/rollup) from 2.79.2 to 2.80.0.
- [Release notes](https://github.com/rollup/rollup/releases)
- [Changelog](https://github.com/rollup/rollup/blob/v2.80.0/CHANGELOG.md)
- [Commits](https://github.com/rollup/rollup/compare/v2.79.2...v2.80.0)

---
updated-dependencies:
- dependency-name: rollup
  dependency-version: 2.80.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-16 14:49:56 +00:00
Cody Thomas 180c6e1914 Merge pull request #511 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/lodash-4.17.23
Bump lodash from 4.17.21 to 4.17.23 in /MythicReactUI
2026-03-16 09:49:35 -05:00
Cody Thomas 59c97d791f Merge pull request #523 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/multi-ca49cfd856
Bump minimatch in /MythicReactUI
2026-03-16 09:49:10 -05:00
Cody Thomas d9e1d5e6bd Merge pull request #497 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/node-forge-1.3.2
Bump node-forge from 1.3.1 to 1.3.2 in /MythicReactUI
2026-03-16 09:48:47 -05:00
Cody Thomas 0f5746b4a3 Merge pull request #514 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/webpack-5.105.0
Bump webpack from 5.99.6 to 5.105.0 in /MythicReactUI
2026-03-16 09:48:27 -05:00
dependabot[bot] 71c03251fb Bump minimatch in /MythicReactUI
Bumps  and [minimatch](https://github.com/isaacs/minimatch). These dependencies needed to be updated together.

Updates `minimatch` from 3.1.2 to 3.1.5
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5)

Updates `minimatch` from 9.0.5 to 9.0.9
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5)

Updates `minimatch` from 5.1.6 to 5.1.9
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.1.2...v3.1.5)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-version: 3.1.5
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 9.0.9
  dependency-type: indirect
- dependency-name: minimatch
  dependency-version: 5.1.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-16 14:48:19 +00:00
dependabot[bot] f742ab3d0f Bump go.opentelemetry.io/otel/sdk in /Mythic_CLI/src
Bumps [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) from 1.25.0 to 1.40.0.
- [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases)
- [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.25.0...v1.40.0)

---
updated-dependencies:
- dependency-name: go.opentelemetry.io/otel/sdk
  dependency-version: 1.40.0
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-03-16 14:48:09 +00:00
Cody Thomas d3ae12a220 Merge pull request #515 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/jsonpath-1.2.1
Bump jsonpath from 1.1.1 to 1.2.1 in /MythicReactUI
2026-03-16 09:47:31 -05:00
Cody Thomas d9a41572ac Merge pull request #517 from its-a-feature/dependabot/npm_and_yarn/MythicReactUI/multi-a07fd7252a
Bump qs and express in /MythicReactUI
2026-03-16 09:47:07 -05:00
github-actions e454531999 Bump Mythic Dockerfile tag to match release 'v3.4.0.46' 2026-03-16 14:45:43 +00:00
its-a-feature 45cf6a9cd8 fixing bug from bad staging 2026-03-16 09:24:21 -05:00
github-actions 5011314ba5 Bump Mythic Dockerfile tag to match release 'v3.4.0.45' 2026-03-12 20:32:09 +00:00
its-a-feature af9d3c40e6 fixing callback imports and scrolling pages/tabs 2026-03-12 15:12:15 -05:00
dependabot[bot] 44d8fd168e Bump qs and express in /MythicReactUI
Bumps [qs](https://github.com/ljharb/qs) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.

Updates `qs` from 6.13.0 to 6.14.2
- [Changelog](https://github.com/ljharb/qs/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ljharb/qs/compare/v6.13.0...v6.14.2)

Updates `express` from 4.21.2 to 4.22.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/v4.22.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.21.2...v4.22.1)

---
updated-dependencies:
- dependency-name: qs
  dependency-version: 6.14.2
  dependency-type: indirect
- dependency-name: express
  dependency-version: 4.22.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-14 03:50:56 +00:00
dependabot[bot] 04ebb6aba7 Bump jsonpath from 1.1.1 to 1.2.1 in /MythicReactUI
Bumps [jsonpath](https://github.com/dchester/jsonpath) from 1.1.1 to 1.2.1.
- [Commits](https://github.com/dchester/jsonpath/commits/1.2.1)

---
updated-dependencies:
- dependency-name: jsonpath
  dependency-version: 1.2.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-12 15:31:41 +00:00
dependabot[bot] 6649279e3e Bump webpack from 5.99.6 to 5.105.0 in /MythicReactUI
Bumps [webpack](https://github.com/webpack/webpack) from 5.99.6 to 5.105.0.
- [Release notes](https://github.com/webpack/webpack/releases)
- [Changelog](https://github.com/webpack/webpack/blob/main/CHANGELOG.md)
- [Commits](https://github.com/webpack/webpack/compare/v5.99.6...v5.105.0)

---
updated-dependencies:
- dependency-name: webpack
  dependency-version: 5.105.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-02-07 14:17:12 +00:00
dependabot[bot] afdd0ec9cc Bump lodash from 4.17.21 to 4.17.23 in /MythicReactUI
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.21 to 4.17.23.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.21...4.17.23)

---
updated-dependencies:
- dependency-name: lodash
  dependency-version: 4.17.23
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2026-01-21 23:47:42 +00:00
dependabot[bot] 7f225e51f1 Bump js-yaml in /MythicReactUI
Bumps  and [js-yaml](https://github.com/nodeca/js-yaml). These dependencies needed to be updated together.

Updates `js-yaml` from 3.14.1 to 3.14.2
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

Updates `js-yaml` from 4.1.0 to 4.1.1
- [Changelog](https://github.com/nodeca/js-yaml/blob/master/CHANGELOG.md)
- [Commits](https://github.com/nodeca/js-yaml/compare/3.14.1...3.14.2)

---
updated-dependencies:
- dependency-name: js-yaml
  dependency-version: 3.14.2
  dependency-type: indirect
- dependency-name: js-yaml
  dependency-version: 4.1.1
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-12-10 19:22:35 +00:00
dependabot[bot] bf22f85661 Bump node-forge from 1.3.1 to 1.3.2 in /MythicReactUI
Bumps [node-forge](https://github.com/digitalbazaar/forge) from 1.3.1 to 1.3.2.
- [Changelog](https://github.com/digitalbazaar/forge/blob/main/CHANGELOG.md)
- [Commits](https://github.com/digitalbazaar/forge/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: node-forge
  dependency-version: 1.3.2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
2025-11-26 22:19:13 +00:00
137 changed files with 2387 additions and 2175 deletions

No files matched your search

+3
View File
@@ -167,3 +167,6 @@ _ReSharper*/
jupyter-docker/jupyter/MythicExamples/.ipynb_checkpoints/
nginx-docker/config/blockips.conf
/MythicReactUI/build/
/MythicReactUI/node_modules/
/.gocache/
+78
View File
@@ -4,6 +4,84 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [3.4.36] - 2026-06-21
### Changed
- Addressed 3 issues from GitHub about spectators and overly scoped hasura filters
## [3.4.35] - 2026-04-16
### Changed
- Merged PR #546 to address missing return in GenerateAPITokenWebhook that would allow non-admin users to generate API Tokens for Bot accounts
- Added server name to emitted log messages
- Merged PR #545 to address missing operation_id in task's log messages for logging containers
- Updated C2 profile code in the UI to properly handle `ui_position` from C2 Profile parameters in payload build
## [3.4.34] - 2026-04-13
### Changed
- Updated way that SendAllOperationsMessage functions to reduce duplicate messages to event log
- Reduced the number of lookups per error message to increment count by caching error lookup data
- Updated event log messages to ensure proper UTF8 encoding (and removal of bad characters)
- Using channels to ensure no duplicate messages are created due to go concurrency
## [3.4.33] - 2026-04-10
### Changed
- Fixed an issue where payload/command syncing would error out and leave dangling database connections
- Updated the postgres docker image to use customized database info for health checks
## [3.4.32] - 2026-04-06
### Changed
- Merged PR to add GET route for /auth_acs/:containerName/:IDPName that was POST only
## [3.4.31] - 2026-03-23
### Changed
- Removed code in `upload` to overwrite uploaded filename with destination filename in filemeta tracking
- Updated `download` code to only write files to disk on new chunks for speed improvements
- Updated the `rm` tracking to not blanket wildcard paths, but first check if it's a directory and make sure there's a trailing slash before wildcarding
## [3.4.30] - 2026-03-20
### Changed
- Added a `received_chunk_ids` map to the database for `filemeta` to track not just chunks, but which chunks are received
- this helps prevent duplicate chunks getting counted multiple times and causing faulty downloads
- Added some default values for `DisplayPath` that were causing null constraint errors on the database
- Fixed a remote path null value with uploading eventing files
## [3.4.29] - 2026-03-18
### Changed
- Adjusted the MythicRPCFileSearch function to not return tracked copies of existing files
## [3.4.28] - 2026-03-17
### Changed
- Fixed a bug where file transfers would fail if no remote path supplied
## [3.4.27] - 2026-03-16
### Changed
- Fixed a bug where mythic would try to write to a null message after processing a bad message
## [3.4.26] - 2026-03-12
### Changed
- Fixed a bug in "Callback Imports" that wouldn't recognize exising build parameter types
## [3.4.25] - 2026-03-04
### Changed
+69
View File
@@ -4,6 +4,75 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [0.3.113] - 2026-07-01
### Changed
- Fixed a bug in the process browser that would set a filter to undefined instead of ""
## [0.3.112] - 2026-05-17
- Updated the single task view to show interactive tasking surrounding results
## [0.3.111] - 2026-05-07
### Changed
- Fixed an issue with useLazyQuery spamming requests
## [0.3.110] - 2026-04-16
### Changed
- Added more generic MythicPageBody and MythicServerHeader components to wrap most pages/headers
- Added a new border color you can select for borders of a few components
- Updated a few places with hard-coded px values to more scalable rem values
## [0.3.109] - 2026-04-10
### Changed
- Updated the UI to sort c2 profile parameters during payload build by ui_position if specified
## [0.3.107] - 2026-03-23
### Changed
- Fixed typo in file browser for remove_folder
## [0.3.106] - 2026-03-21
### Changed
- Fixed an issue with clicking file uploads in modals
## [0.3.105] - 2026-03-20
### Changed
- Updated some of the file upload pieces in the UI to support drag-and-drop
- Added an `autoScroll` button to interactive tasking so you can force auto scroll or not
- Fixed some of the drag and drop modal components
## [0.3.104] - 2026-03-19
### Changed
- Added ability to drag and move some modals and copy data from behind them
## [0.3.103] - 2026-03-17
### Changed
- Fixed issue with file uploads for payload/wrapper builds when starting from existing payload
## [0.3.102] - 2026-03-12
### Changed
- Slight tweak to `tabs` browser script capability to cap the max size of the tabs
- Fixed a scrolling issue on the single task view page
## [0.3.101] - 2026-02-19
### Changed
+226 -276
View File
@@ -45,7 +45,7 @@
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-moment": "^1.1.3",
"react-router-dom": "^7.5.2",
"react-router-dom": "^7.13.1",
"react-scrollbar-size": "^5.0.0",
"react-split": "^2.0.14",
"react-toastify": "^11.0.5",
@@ -58,7 +58,7 @@
"sql.js": "^1.13.0",
"styled-components": "^6.1.17",
"uuid": "^9.0.1",
"webpack": "^5.99.6",
"webpack": "^5.105.0",
"word-wrap": "^1.2.5"
},
"devDependencies": {
@@ -2811,9 +2811,9 @@
}
},
"node_modules/@eslint/eslintrc/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -4703,9 +4703,9 @@
}
},
"node_modules/@types/estree": {
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT"
},
"node_modules/@types/express": {
@@ -5548,9 +5548,9 @@
"license": "BSD-3-Clause"
},
"node_modules/acorn": {
"version": "8.14.1",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz",
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==",
"version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"license": "MIT",
"bin": {
"acorn": "bin/acorn"
@@ -5583,6 +5583,18 @@
"node": ">=0.4.0"
}
},
"node_modules/acorn-import-phases": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/acorn-import-phases/-/acorn-import-phases-1.0.4.tgz",
"integrity": "sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==",
"license": "MIT",
"engines": {
"node": ">=10.13.0"
},
"peerDependencies": {
"acorn": "^8.14.0"
}
},
"node_modules/acorn-jsx": {
"version": "5.3.2",
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
@@ -5641,9 +5653,9 @@
}
},
"node_modules/ajv": {
"version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
"version": "6.14.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
"integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -5675,9 +5687,9 @@
}
},
"node_modules/ajv-formats/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -6473,6 +6485,15 @@
],
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
"version": "2.9.19",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz",
"integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==",
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.js"
}
},
"node_modules/batch": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
@@ -6775,9 +6796,9 @@
"license": "MIT"
},
"node_modules/browserslist": {
"version": "4.24.4",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
"integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
"version": "4.28.1",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
"integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
"funding": [
{
"type": "opencollective",
@@ -6794,10 +6815,11 @@
],
"license": "MIT",
"dependencies": {
"caniuse-lite": "^1.0.30001688",
"electron-to-chromium": "^1.5.73",
"node-releases": "^2.0.19",
"update-browserslist-db": "^1.1.1"
"baseline-browser-mapping": "^2.9.0",
"caniuse-lite": "^1.0.30001759",
"electron-to-chromium": "^1.5.263",
"node-releases": "^2.0.27",
"update-browserslist-db": "^1.2.0"
},
"bin": {
"browserslist": "cli.js"
@@ -6993,9 +7015,9 @@
}
},
"node_modules/caniuse-lite": {
"version": "1.0.30001748",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001748.tgz",
"integrity": "sha512-5P5UgAr0+aBmNiplks08JLw+AW/XG/SurlgZLgB1dDLfAw7EfRGxIwzPHxdSCGY/BTKDqIVyJL87cCN6s0ZR0w==",
"version": "1.0.30001769",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001769.tgz",
"integrity": "sha512-BCfFL1sHijQlBGWBMuJyhZUhzo7wer5sVj9hqekB/7xn0Ypy+pER/edCYQm4exbXj4WiySGp40P8UuTh6w1srg==",
"funding": [
{
"type": "opencollective",
@@ -7485,12 +7507,16 @@
"license": "MIT"
},
"node_modules/cookie": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
"integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"license": "MIT",
"engines": {
"node": ">=18"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
}
},
"node_modules/cookie-signature": {
@@ -8869,9 +8895,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.5.142",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.142.tgz",
"integrity": "sha512-Ah2HgkTu/9RhTDNThBtzu2Wirdy4DC9b0sMT1pUhbkZQ5U/iwmE+PHZX1MpjD5IkJCc2wSghgGG/B04szAx07w==",
"version": "1.5.286",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.286.tgz",
"integrity": "sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==",
"license": "ISC"
},
"node_modules/elkjs": {
@@ -8944,13 +8970,13 @@
}
},
"node_modules/enhanced-resolve": {
"version": "5.18.1",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
"integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
"version": "5.19.0",
"resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz",
"integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==",
"license": "MIT",
"dependencies": {
"graceful-fs": "^4.2.4",
"tapable": "^2.2.0"
"tapable": "^2.3.0"
},
"engines": {
"node": ">=10.13.0"
@@ -9107,9 +9133,9 @@
}
},
"node_modules/es-module-lexer": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
"integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz",
"integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==",
"license": "MIT"
},
"node_modules/es-object-atoms": {
@@ -9741,9 +9767,9 @@
}
},
"node_modules/eslint/node_modules/js-yaml": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
"integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -9984,40 +10010,40 @@
}
},
"node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
"integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
"version": "4.22.1",
"resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
"integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
"dev": true,
"license": "MIT",
"dependencies": {
"accepts": "~1.3.8",
"array-flatten": "1.1.1",
"body-parser": "1.20.3",
"content-disposition": "0.5.4",
"body-parser": "~1.20.3",
"content-disposition": "~0.5.4",
"content-type": "~1.0.4",
"cookie": "0.7.1",
"cookie-signature": "1.0.6",
"cookie": "~0.7.1",
"cookie-signature": "~1.0.6",
"debug": "2.6.9",
"depd": "2.0.0",
"encodeurl": "~2.0.0",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "1.3.1",
"fresh": "0.5.2",
"http-errors": "2.0.0",
"finalhandler": "~1.3.1",
"fresh": "~0.5.2",
"http-errors": "~2.0.0",
"merge-descriptors": "1.0.3",
"methods": "~1.1.2",
"on-finished": "2.4.1",
"on-finished": "~2.4.1",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.12",
"path-to-regexp": "~0.1.12",
"proxy-addr": "~2.0.7",
"qs": "6.13.0",
"qs": "~6.14.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.2.1",
"send": "0.19.0",
"serve-static": "1.16.2",
"send": "~0.19.0",
"serve-static": "~1.16.2",
"setprototypeof": "1.2.0",
"statuses": "2.0.1",
"statuses": "~2.0.1",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
@@ -10057,6 +10083,22 @@
"dev": true,
"license": "MIT"
},
"node_modules/express/node_modules/qs": {
"version": "6.14.2",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
"integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
"side-channel": "^1.1.0"
},
"engines": {
"node": ">=0.6"
},
"funding": {
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -10230,9 +10272,9 @@
}
},
"node_modules/filelist/node_modules/minimatch": {
"version": "5.1.6",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
"version": "5.1.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
"integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -10355,16 +10397,16 @@
}
},
"node_modules/flatted": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
"integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
"version": "3.4.2",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz",
"integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==",
"dev": true,
"license": "ISC"
},
"node_modules/follow-redirects": {
"version": "1.15.9",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
"integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
"version": "1.16.0",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz",
"integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
"dev": true,
"funding": [
{
@@ -13338,9 +13380,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
"version": "3.14.1",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz",
"integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==",
"version": "3.14.2",
"resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz",
"integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -13492,21 +13534,21 @@
}
},
"node_modules/jsonpath": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.1.1.tgz",
"integrity": "sha512-l6Cg7jRpixfbgoWgkrl77dgEj8RPvND0wMH6TwQmi9Qs4TFfS9u5cUFnbeKTwj5ga5Y3BTGGNI28k117LJ009w==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/jsonpath/-/jsonpath-1.3.0.tgz",
"integrity": "sha512-0kjkYHJBkAy50Z5QzArZ7udmvxrJzkpKYW27fiF//BrMY7TQibYLl+FYIXN2BiYmwMIVzSfD8aDRj6IzgBX2/w==",
"dev": true,
"license": "MIT",
"dependencies": {
"esprima": "1.2.2",
"static-eval": "2.0.2",
"underscore": "1.12.1"
"esprima": "1.2.5",
"static-eval": "2.1.1",
"underscore": "1.13.6"
}
},
"node_modules/jsonpath/node_modules/esprima": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.2.tgz",
"integrity": "sha512-+JpPZam9w5DuJ3Q67SqsMGtiHKENSMRVoxvArfJZK01/BfLEObtZ6orJa/MtoGNR/rfMgp5837T41PAmTwAv/A==",
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz",
"integrity": "sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ==",
"dev": true,
"bin": {
"esparse": "bin/esparse.js",
@@ -13663,12 +13705,16 @@
"license": "MIT"
},
"node_modules/loader-runner": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz",
"integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==",
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
"integrity": "sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==",
"license": "MIT",
"engines": {
"node": ">=6.11.5"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/loader-utils": {
@@ -13700,9 +13746,9 @@
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
"version": "4.18.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
"integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
"dev": true,
"license": "MIT"
},
@@ -14042,9 +14088,9 @@
"license": "MIT"
},
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"version": "3.1.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
"integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
"license": "ISC",
"dependencies": {
@@ -14200,9 +14246,9 @@
}
},
"node_modules/node-forge": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz",
"integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==",
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.4.0.tgz",
"integrity": "sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==",
"dev": true,
"license": "(BSD-3-Clause OR GPL-2.0)",
"engines": {
@@ -14217,9 +14263,9 @@
"license": "MIT"
},
"node_modules/node-releases": {
"version": "2.0.19",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
"version": "2.0.27",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
"integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
"license": "MIT"
},
"node_modules/normalize-path": {
@@ -14824,9 +14870,9 @@
"license": "ISC"
},
"node_modules/picomatch": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz",
"integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -15508,16 +15554,19 @@
}
},
"node_modules/postcss-load-config/node_modules/yaml": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
"integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz",
"integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==",
"dev": true,
"license": "ISC",
"bin": {
"yaml": "bin.mjs"
},
"engines": {
"node": ">= 14"
"node": ">= 14.6"
},
"funding": {
"url": "https://github.com/sponsors/eemeli"
}
},
"node_modules/postcss-loader": {
@@ -16961,14 +17010,13 @@
}
},
"node_modules/react-router": {
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.5.2.tgz",
"integrity": "sha512-9Rw8r199klMnlGZ8VAsV/I8WrIF6IyJ90JQUdboupx1cdkgYqwnrYjH+I/nY/7cA1X5zia4mDJqH36npP7sxGQ==",
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz",
"integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==",
"license": "MIT",
"dependencies": {
"cookie": "^1.0.1",
"set-cookie-parser": "^2.6.0",
"turbo-stream": "2.4.0"
"set-cookie-parser": "^2.6.0"
},
"engines": {
"node": ">=20.0.0"
@@ -16984,12 +17032,12 @@
}
},
"node_modules/react-router-dom": {
"version": "7.5.2",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.5.2.tgz",
"integrity": "sha512-yk1XW8Fj7gK7flpYBXF3yzd2NbX6P7Kxjvs2b5nu1M04rb5pg/Zc4fGdBNTeT4eDYL2bvzWNyKaIMJX/RKHTTg==",
"version": "7.13.1",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz",
"integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==",
"license": "MIT",
"dependencies": {
"react-router": "7.5.2"
"react-router": "7.13.1"
},
"engines": {
"node": ">=20.0.0"
@@ -17727,9 +17775,9 @@
"license": "Unlicense"
},
"node_modules/rollup": {
"version": "2.79.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
"integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
"version": "2.80.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz",
"integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==",
"dev": true,
"license": "MIT",
"bin": {
@@ -17972,9 +18020,9 @@
}
},
"node_modules/schema-utils": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz",
"integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==",
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz",
"integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==",
"license": "MIT",
"dependencies": {
"@types/json-schema": "^7.0.9",
@@ -17991,9 +18039,9 @@
}
},
"node_modules/schema-utils/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"license": "MIT",
"dependencies": {
"fast-deep-equal": "^3.1.3",
@@ -18221,9 +18269,9 @@
}
},
"node_modules/set-cookie-parser": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
"version": "2.7.2",
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
"integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
"license": "MIT"
},
"node_modules/set-function-length": {
@@ -18631,111 +18679,13 @@
"license": "MIT"
},
"node_modules/static-eval": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.0.2.tgz",
"integrity": "sha512-N/D219Hcr2bPjLxPiV+TQE++Tsmrady7TqAJugLy7Xk1EumfDWS/f5dtBbkRCGE7wKKXuYockQoj8Rm2/pVKyg==",
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/static-eval/-/static-eval-2.1.1.tgz",
"integrity": "sha512-MgWpQ/ZjGieSVB3eOJVs4OA2LT/q1vx98KPCTTQPzq/aLr0YUXTsgryTXr4SLfR0ZfUUCiedM9n/ABeDIyy4mA==",
"dev": true,
"license": "MIT",
"dependencies": {
"escodegen": "^1.8.1"
}
},
"node_modules/static-eval/node_modules/escodegen": {
"version": "1.14.3",
"resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
"integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"esprima": "^4.0.1",
"estraverse": "^4.2.0",
"esutils": "^2.0.2",
"optionator": "^0.8.1"
},
"bin": {
"escodegen": "bin/escodegen.js",
"esgenerate": "bin/esgenerate.js"
},
"engines": {
"node": ">=4.0"
},
"optionalDependencies": {
"source-map": "~0.6.1"
}
},
"node_modules/static-eval/node_modules/estraverse": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true,
"license": "BSD-2-Clause",
"engines": {
"node": ">=4.0"
}
},
"node_modules/static-eval/node_modules/levn": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
"integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==",
"dev": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/static-eval/node_modules/optionator": {
"version": "0.8.3",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz",
"integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"deep-is": "~0.1.3",
"fast-levenshtein": "~2.0.6",
"levn": "~0.3.0",
"prelude-ls": "~1.1.2",
"type-check": "~0.3.2",
"word-wrap": "~1.2.3"
},
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/static-eval/node_modules/prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
"integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==",
"dev": true,
"engines": {
"node": ">= 0.8.0"
}
},
"node_modules/static-eval/node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
"optional": true,
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/static-eval/node_modules/type-check": {
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
"integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==",
"dev": true,
"license": "MIT",
"dependencies": {
"prelude-ls": "~1.1.2"
},
"engines": {
"node": ">= 0.8.0"
"escodegen": "^2.1.0"
}
},
"node_modules/statuses": {
@@ -19186,9 +19136,9 @@
}
},
"node_modules/sucrase/node_modules/brace-expansion": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -19227,13 +19177,13 @@
}
},
"node_modules/sucrase/node_modules/minimatch": {
"version": "9.0.5",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"version": "9.0.9",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
"integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
"dev": true,
"license": "ISC",
"dependencies": {
"brace-expansion": "^2.0.1"
"brace-expansion": "^2.0.2"
},
"engines": {
"node": ">=16 || 14 >=14.17"
@@ -19528,12 +19478,16 @@
}
},
"node_modules/tapable": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
"integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
"integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"license": "MIT",
"engines": {
"node": ">=6"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/webpack"
}
},
"node_modules/temp-dir": {
@@ -19614,9 +19568,9 @@
}
},
"node_modules/terser-webpack-plugin": {
"version": "5.3.14",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz",
"integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==",
"version": "5.3.16",
"resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.16.tgz",
"integrity": "sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==",
"license": "MIT",
"dependencies": {
"@jridgewell/trace-mapping": "^0.3.25",
@@ -19902,12 +19856,6 @@
"dev": true,
"license": "0BSD"
},
"node_modules/turbo-stream": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz",
"integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==",
"license": "ISC"
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -20081,9 +20029,9 @@
}
},
"node_modules/underscore": {
"version": "1.12.1",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz",
"integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==",
"version": "1.13.6",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz",
"integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==",
"dev": true,
"license": "MIT"
},
@@ -20189,9 +20137,9 @@
}
},
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
"integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
"funding": [
{
"type": "opencollective",
@@ -20389,9 +20337,9 @@
}
},
"node_modules/watchpack": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.2.tgz",
"integrity": "sha512-TnbFSbcOCcDgjZ4piURLCbJ3nJhznVh9kw6F6iokjiFPl8ONxe9A6nMDVXDiNbrSfLILs6vB07F7wLBrwPYzJw==",
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.5.1.tgz",
"integrity": "sha512-Zn5uXdcFNIA1+1Ei5McRd+iRzfhENPCe7LeABkJtNulSxjma+l7ltNx55BWZkRlwRnpOgHqxnjyaDgJnNXnqzg==",
"license": "MIT",
"dependencies": {
"glob-to-regexp": "^0.4.1",
@@ -20422,34 +20370,36 @@
}
},
"node_modules/webpack": {
"version": "5.99.6",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.6.tgz",
"integrity": "sha512-TJOLrJ6oeccsGWPl7ujCYuc0pIq2cNsuD6GZDma8i5o5Npvcco/z+NKvZSFsP0/x6SShVb0+X2JK/JHUjKY9dQ==",
"version": "5.105.0",
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.105.0.tgz",
"integrity": "sha512-gX/dMkRQc7QOMzgTe6KsYFM7DxeIONQSui1s0n/0xht36HvrgbxtM1xBlgx596NbpHuQU8P7QpKwrZYwUX48nw==",
"license": "MIT",
"dependencies": {
"@types/eslint-scope": "^3.7.7",
"@types/estree": "^1.0.6",
"@types/estree": "^1.0.8",
"@types/json-schema": "^7.0.15",
"@webassemblyjs/ast": "^1.14.1",
"@webassemblyjs/wasm-edit": "^1.14.1",
"@webassemblyjs/wasm-parser": "^1.14.1",
"acorn": "^8.14.0",
"browserslist": "^4.24.0",
"acorn": "^8.15.0",
"acorn-import-phases": "^1.0.3",
"browserslist": "^4.28.1",
"chrome-trace-event": "^1.0.2",
"enhanced-resolve": "^5.17.1",
"es-module-lexer": "^1.2.1",
"enhanced-resolve": "^5.19.0",
"es-module-lexer": "^2.0.0",
"eslint-scope": "5.1.1",
"events": "^3.2.0",
"glob-to-regexp": "^0.4.1",
"graceful-fs": "^4.2.11",
"json-parse-even-better-errors": "^2.3.1",
"loader-runner": "^4.2.0",
"loader-runner": "^4.3.1",
"mime-types": "^2.1.27",
"neo-async": "^2.6.2",
"schema-utils": "^4.3.0",
"tapable": "^2.1.1",
"terser-webpack-plugin": "^5.3.11",
"watchpack": "^2.4.1",
"webpack-sources": "^3.2.3"
"schema-utils": "^4.3.3",
"tapable": "^2.3.0",
"terser-webpack-plugin": "^5.3.16",
"watchpack": "^2.5.1",
"webpack-sources": "^3.3.3"
},
"bin": {
"webpack": "bin/webpack.js"
@@ -20593,9 +20543,9 @@
}
},
"node_modules/webpack-sources": {
"version": "3.2.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz",
"integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==",
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz",
"integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==",
"license": "MIT",
"engines": {
"node": ">=10.13.0"
@@ -20903,9 +20853,9 @@
}
},
"node_modules/workbox-build/node_modules/ajv": {
"version": "8.17.1",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
"version": "8.18.0",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
"integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -21275,9 +21225,9 @@
"license": "ISC"
},
"node_modules/yaml": {
"version": "1.10.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
"version": "1.10.3",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz",
"integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==",
"license": "ISC",
"engines": {
"node": ">= 6"
+2 -2
View File
@@ -50,7 +50,7 @@
"react-dom": "^18.3.1",
"react-draggable": "^4.4.6",
"react-moment": "^1.1.3",
"react-router-dom": "^7.5.2",
"react-router-dom": "^7.13.1",
"react-scrollbar-size": "^5.0.0",
"react-split": "^2.0.14",
"react-toastify": "^11.0.5",
@@ -63,7 +63,7 @@
"sql.js": "^1.13.0",
"styled-components": "^6.1.17",
"uuid": "^9.0.1",
"webpack": "^5.99.6",
"webpack": "^5.105.0",
"word-wrap": "^1.2.5"
},
"scripts": {
+4
View File
@@ -153,6 +153,10 @@ export const operatorSettingDefaults = {
outputTextColor: {
dark: '#f6f6f6',
light: '#282828',
},
borderColor: {
dark: "#595858",
light: "#c5c5c5"
}
},
}
+8
View File
@@ -40,6 +40,8 @@ import {snackActions} from "./utilities/Snackbar";
import {TopAppBarVertical} from "./TopAppBarVertical";
import { library } from '@fortawesome/fontawesome-svg-core';
import * as Icons from '@fortawesome/free-solid-svg-icons';
import {Jupyter} from "./pages/Jupyter/Jupyter";
import {Hasura} from "./pages/Hasura/Hasura";
// add all fas icons
const iconList = Object
@@ -164,6 +166,8 @@ export function App(props) {
preferences?.palette?.outputBackgroundColor?.light || operatorSettingDefaults.palette.outputBackgroundColor.light,
outputTextColor: themeMode === 'dark' ? preferences?.palette?.outputTextColor?.dark || operatorSettingDefaults.palette.outputTextColor.dark :
preferences?.palette?.outputTextColor?.light || operatorSettingDefaults.palette.outputTextColor.light,
borderColor: themeMode === 'dark' ? preferences?.palette?.borderColor?.dark || operatorSettingDefaults.palette.borderColor.dark :
preferences?.palette?.borderColor?.light || operatorSettingDefaults.palette.borderColor.light,
})
}catch(error){
console.log(error);
@@ -249,6 +253,8 @@ export function App(props) {
operatorSettingDefaults.palette.outputBackgroundColor.light,
outputTextColor: themeMode === 'dark' ? operatorSettingDefaults.palette.outputTextColor.dark :
operatorSettingDefaults.palette.outputTextColor.light,
borderColor: themeMode === 'dark' ? operatorSettingDefaults.palette.borderColor.dark :
operatorSettingDefaults.palette.borderColor.light,
typography: {
fontSize: 12,//operatorSettingDefaults?.fontSize,
fontFamily: operatorSettingDefaults?.fontFamily
@@ -435,6 +441,8 @@ export function App(props) {
element={<LoggedInRoute me={me}><Tags me={me}/></LoggedInRoute>}/>
<Route exact path='/new/eventing'
element={<LoggedInRoute me={me}><Eventing me={me}/></LoggedInRoute>}/>
<Route exact path='/new/jupyter' element={<LoggedInRoute me={me}><Jupyter/></LoggedInRoute>}/>
<Route exact path='/new/hasura' element={<LoggedInRoute me={me}><Hasura/></LoggedInRoute>}/>
</Routes>
</div>
</div>
@@ -21,10 +21,26 @@ import {useTheme} from '@mui/material/styles';
import WrapTextIcon from '@mui/icons-material/WrapText';
import {IconButton} from '@mui/material';
import {MythicStyledTooltip} from "./MythicStyledTooltip";
import Draggable from 'react-draggable';
import {MythicDraggableDialogTitle} from "./MythicDraggableDialogTitle";
export function MythicDialog(props) {
const descriptionElementRef = React.useRef(null);
React.useEffect(() => {
const [draggedState, setDraggedState] = React.useState({
style: {},
paperStyle: {
margin: "0",
height: "fit-content",
width: "stretch"
},
containerStyle: {
},
hideBackdrop: false,
modified: false
});
const nodeRef = React.useRef(null);
const descriptionElementRef = React.useRef(null);
React.useEffect(() => {
if (props.open) {
const { current: descriptionElement } = descriptionElementRef;
if (descriptionElement !== null) {
@@ -33,30 +49,91 @@ export function MythicDialog(props) {
}
}, [props.open]);
const dialogOnClick = (e) => {
e.stopPropagation();
//e.preventDefault();
//e.stopPropagation();
if(e.target.classList.length > 0 && e.target.classList.contains("MuiDialog-container")){
props.onClose();
props.onClose();
}
}
const dialogOnContextMenu = (e) => {
e.stopPropagation();
}
const handleOnClose = (event, reason) => {
if(reason === "backdropClick" && draggedState.hideBackdrop){
return;
}
props.onClose();
}
const onStart = (e) => {
if(e){
e.stopPropagation();
e.preventDefault();
}
if(!draggedState.modified){
setDraggedState({
style: {
height: e.target.offsetParent.offsetHeight + "px",
width: e.target.offsetParent.offsetWidth + "px",
margin: "auto",
overflowY: "auto",
},
paperStyle: {
//height: e.target.offsetParent.offsetHeight + "px",
width: e.target.offsetParent.offsetWidth + "px",
margin: 0,
overflowY: "auto",
},
containerStyle: {
height: "fit-content",
overflowY: "auto"
},
hideBackdrop: true,
modified: true,
})
}
}
const onStop = (e) => {
if(e){
e.stopPropagation();
e.preventDefault();
}
}
return (
<Dialog
open={props.open}
onClose={props.onClose}
scroll="paper"
maxWidth={props.maxWidth}
fullWidth={props.fullWidth}
style={props.style}
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
onMouseDown={dialogOnClick}
onContextMenu={dialogOnContextMenu}
<Draggable
nodeRef={nodeRef}
handle="#mythic-draggable-title"
cancel={'[class*="MuiDialogContent-root"]'}
onStart={onStart}
onStop={onStop}
>
{props.innerDialog}
</Dialog>
<Dialog
ref={nodeRef}
open={props.open}
onClick={dialogOnClick}
onClose={handleOnClose}
scroll="paper"
maxWidth={props.maxWidth}
fullWidth={true}
style={{...props.style, ...draggedState.style}}
disableEnforceFocus={true}
disablePortal={false}
hideBackdrop={draggedState.hideBackdrop}
aria-labelledby="scroll-dialog-title"
aria-describedby="scroll-dialog-description"
sx={{
[".MuiPaper-root"]: {
...draggedState.paperStyle
},
[".MuiDialog-container"]: {
...draggedState.containerStyle
}
}}
onMouseDown={dialogOnClick}
onContextMenu={dialogOnContextMenu}
>
{props.innerDialog}
</Dialog>
</Draggable>
);
}
@@ -85,17 +162,16 @@ export function MythicModifyStringDialog(props) {
return (
<React.Fragment>
{props.title !== "" &&
<DialogTitle id="form-dialog-title">{props.title}
<MythicDraggableDialogTitle>{props.title}
<MythicStyledTooltip title={wrap ? "Toggle off word wrap" : "Toggl on word wrap"}
tooltipStyle={{float: "right"}}>
<IconButton onClick={() => {setWrap(!wrap)}}>
<WrapTextIcon color={wrap ? "success" : "secondary"} />
</IconButton>
</MythicStyledTooltip>
</DialogTitle>
</MythicDraggableDialogTitle>
}
<DialogContent dividers={true} style={{height: "100%", margin: 0, padding: 0}}>
<DialogContent dividers={true} style={{ margin: 0, padding: 0}}>
<AceEditor
mode="json"
theme={theme.palette.mode === 'dark' ? 'monokai' : 'github'}
@@ -198,7 +274,7 @@ export function MythicViewJSONAsTableDialog(props) {
}, [props.value, props.leftColumn, props.rightColumn]);
return (
<React.Fragment>
<DialogTitle id="form-dialog-title" style={{wordBreak: "break-all", maxWidth: "100%"}}>{props.title}</DialogTitle>
<MythicDraggableDialogTitle style={{wordBreak: "break-all", maxWidth: "100%"}}>{props.title}</MythicDraggableDialogTitle>
<TableContainer className="mythicElement" style={{paddingLeft: "10px"}}>
<Table size="small" style={{"tableLayout": "fixed", "maxWidth": "calc(100vw)", "overflow": "scroll"}}>
@@ -0,0 +1,10 @@
import DialogTitle from '@mui/material/DialogTitle';
export const MythicDraggableDialogTitle = ({children}) => {
return (
<DialogTitle id="mythic-draggable-title" style={{ cursor: 'move', width: "100%" }}>
{children}
</DialogTitle>
)
}
@@ -71,6 +71,13 @@ export const MythicFileContext = ({agent_file_id, display_link, filename, extraS
if(fileData.agent_file_id === '' || fileData.filename === undefined){
return agent_file_id
}
const onClose = (e) => {
if(e){
e.preventDefault();
e.stopPropagation();
}
setOpenPreviewMediaDialog(false)
}
return (
<>
<MythicStyledTooltip title={"Preview Media"} tooltipStyle={extraStyles ? extraStyles : {}}>
@@ -83,11 +90,11 @@ export const MythicFileContext = ({agent_file_id, display_link, filename, extraS
</Link>
{openPreviewMediaDialog &&
<MythicDialog fullWidth={true} maxWidth="xl" open={openPreviewMediaDialog}
onClose={(e)=>{setOpenPreviewMediaDialog(false);}}
onClose={onClose}
innerDialog={<PreviewFileMediaDialog
agent_file_id={fileData.agent_file_id}
filename={fileData.filename}
onClose={(e)=>{setOpenPreviewMediaDialog(false);}} />}
onClose={onClose} />}
/>
}
</>
@@ -0,0 +1,13 @@
export const MythicPageBody = ({children}) => {
return (
<div style={{
height: "100%",
display: "flex",
flexDirection: "column",
margin: "0.5rem",
}}>
{children}
</div>
)
}
@@ -0,0 +1,32 @@
import {useTheme} from '@mui/material/styles';
import Paper from '@mui/material/Paper';
import {Typography} from "@mui/material";
export const MythicPageHeader = ({children, title, style={}, headerVariant="h5"}) => {
const theme = useTheme();
return (
<Paper elevation={2}
variant={"elevation"}
style={{
backgroundColor: theme.pageHeader.main,
color: theme.pageHeaderText.main,
border: `1px solid ${theme.borderColor}`,
alignItems: "center",
display: "flex",
justifyContent: "space-between",
marginBottom: "0.5rem",
padding: "0.5rem",
borderRadius: "5px",
...style
}}
>
<Typography variant={headerVariant} style={{textAlign: "left", display: "inline-block", marginLeft: "1rem", width: "100%"}}>
{title}
</Typography>
<div style={{display: "flex", marginRight: "1rem", alignItems: "center"}}>
{children}
</div>
</Paper>
)
}
@@ -29,6 +29,7 @@ import Typography from '@mui/material/Typography';
import MythicStyledTableCell from "./MythicTableCell";
import {meState} from "../../cache";
import { useReactiveVar } from '@apollo/client';
import {MythicDraggableDialogTitle} from "./MythicDraggableDialogTitle";
const createNewTagMutationTemplate = ({target_object}) => {
// target_object should be something like "task_id"
@@ -495,20 +496,17 @@ const onAcceptDelete = () => {
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">Edit Tags
</DialogTitle>
<DialogContent dividers={true}>
{openNewDialog ?
(<MythicDialog fullWidth={true} maxWidth="xl" open={openNewDialog}
<MythicDraggableDialogTitle >Edit Tags </MythicDraggableDialogTitle>
<DialogContent dividers={true} style={{width: "100%"}}>
{openNewDialog && <MythicDialog fullWidth={true} maxWidth="lg" open={openNewDialog}
onClose={()=>{setOpenNewDialog(false);}}
innerDialog={<NewTagDialog me={props.me}
target_object={props.target_object}
target_object_id={props.target_object_id}
onClose={()=>{setOpenNewDialog(false);}}
onSubmit={handleNewTagCreate} />}
/>) : null}
<TableContainer className="mythicElement">
<Table size="small" style={{ "maxWidth": "100%", "overflow": "scroll"}}>
/>}
<Table size="small" style={{ width: "100%", overflow: "scroll"}}>
<TableBody>
<TableRow hover>
<MythicStyledTableCell style={{width: "30%"}}>Select Existing Tag to Edit or Add New</MythicStyledTableCell>
@@ -578,7 +576,6 @@ return (
</TableRow>
</TableBody>
</Table>
</TableContainer>
</DialogContent>
<DialogActions>
<Button onClick={props.onClose} variant="contained" color="primary">
@@ -647,10 +644,9 @@ export function NewTagDialog(props) {
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">Add New Tag</DialogTitle>
<DialogContent dividers={true}>
<TableContainer className="mythicElement">
<Table size="small" style={{ "maxWidth": "100%", "overflow": "scroll"}}>
<MythicDraggableDialogTitle >Add New Tag</MythicDraggableDialogTitle>
<DialogContent dividers={true} style={{width: "100%"}}>
<Table size="small" style={{ overflow: "scroll", width: "100%"}}>
<TableBody>
<TableRow hover>
<MythicStyledTableCell style={{width: "20%"}}>
@@ -717,7 +713,6 @@ export function NewTagDialog(props) {
</TableRow>
</TableBody>
</Table>
</TableContainer>
</DialogContent>
<DialogActions>
<Button onClick={props.onClose} variant="contained" color="primary">
@@ -78,6 +78,8 @@ import {
} from "@hello-pangea/dnd";
import {reorder} from "./MythicComponents/MythicDraggableList";
import { useNavigate } from 'react-router-dom';
import LogoutIcon from '@mui/icons-material/Logout';
import TuneIcon from '@mui/icons-material/Tune';
const PREFIX = 'TopAppBarVertical';
@@ -436,7 +438,7 @@ const Eventing = () => {
}
const JupyterNotebook = () => {
return (
<StyledListItem className={classes.listSubHeader} target="_blank" component={Link} to='/jupyter' key={"jupyter"} >
<StyledListItem className={classes.listSubHeader} target="_blank" component={Link} to='/jupyter' key={"jupyter"} >
<StyledListItemIcon>
<MythicStyledTooltip title={"Jupyter Notebooks"} tooltipStyle={{display: "inline-flex"}}>
<img src={JupyterLogo} height={"25px"} width={"25px"} />
@@ -448,7 +450,7 @@ const JupyterNotebook = () => {
}
const GraphQL = () => {
return (
<StyledListItem className={classes.listSubHeader} target="_blank" component={Link} to='/console' key={"console"} >
<StyledListItem className={classes.listSubHeader} target="_blank" component={Link} to='/console' key={"console"} >
<StyledListItemIcon>
<MythicStyledTooltip title={"GraphQL Console"} tooltipStyle={{display: "inline-flex"}}>
<img src={GraphQLLogo} height={"25px"} width={"25px"} className="mythicElement"/>
@@ -898,16 +900,29 @@ function TopBarRightShortcutsVertical({me, menuOpen, serverName}){
{
name: (
<>
<Typography paragraph={true} variant="caption" style={{marginBottom: "0"}}>Signed in as:</Typography>
<Typography paragraph={true} variant="body1" style={{marginBottom: "0", fontWeight: 600}}> {me?.user?.username || "" } </Typography>
</>
<Typography variant={"caption"} style={{marginBottom: "0"}}>Signed in as:</Typography>
<Typography variant={"body1"} style={{marginBottom: "0", fontWeight: 600}}>{me?.user?.username}</Typography>
</>),
disabled: true,
click: handleSettingsClose
},
{
name: (
<div style={{display: "flex", alignItems: "center"}}>
<TuneIcon style={{marginRight: "0.5rem"}}/>
<Typography variant="body1" style={{marginBottom: "0", }}> Settings </Typography>
</div>
),
to: "/new/settings",
component: Link,
click: handleSettingsClose
},
{
name: "Logout",
name: (
<div style={{display: "flex", alignItems: "center"}}>
<LogoutIcon style={{marginRight: "0.5rem"}} /> Logout
</div>
),
click: handleLogout,
to: "",
}
@@ -4,6 +4,7 @@ import {BrowserScriptsTable} from './BrowserScriptsTable';
import {snackActions} from '../../utilities/Snackbar';
import { Backdrop } from '@mui/material';
import {CircularProgress} from '@mui/material';
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
const SUB_BrowserScripts = gql`
@@ -135,7 +136,7 @@ export function BrowserScripts({me}){
}
}, [])
return (
<React.Fragment>
<MythicPageBody>
<Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute"}} invisible={false}>
<CircularProgress color="inherit" />
</Backdrop>
@@ -145,6 +146,6 @@ export function BrowserScripts({me}){
onSubmitEdit={onSubmitEdit} onRevert={onRevert} onSubmitNew={onSubmitCreateNewBrowserScript}
/>
</React.Fragment>
</MythicPageBody>
);
}
@@ -13,30 +13,27 @@ import AddCircleIcon from '@mui/icons-material/AddCircle';
import Button from '@mui/material/Button';
import { MythicDialog } from '../../MythicComponents/MythicDialog';
import {EditScriptDialog} from './EditScriptDialog';
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
export function BrowserScriptsTable(props){
const theme = useTheme();
const [openNewScriptDialog, setOpenNewScriptDialog] = React.useState(false);
return (
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Browser Scripts
</Typography>
<Button size="small" onClick={() => setOpenNewScriptDialog(true)} style={{float: "right", marginRight: "30px", color: "white"}}
<>
<MythicPageHeader title={"Browser Scripts"}>
<Button size="small" onClick={() => setOpenNewScriptDialog(true)} style={{whiteSpace: "nowrap", color: "white"}}
startIcon={<AddCircleIcon color={"success"} style={{backgroundColor: "white", borderRadius: "10px"}}/>} >
New Script
</Button>
{openNewScriptDialog ? (
{openNewScriptDialog &&
<MythicDialog fullWidth={true} maxWidth="xl" open={openNewScriptDialog}
onClose={()=>{setOpenNewScriptDialog(false);}}
innerDialog={
<EditScriptDialog me={props.me} onClose={()=>{setOpenNewScriptDialog(false);}} title="Create New Browser Script" new={true} onSubmitEdit={props.onSubmitNew} />
} />
) : null
} />
}
</Paper>
</MythicPageHeader>
<TableContainer className="mythicElement" style={{height: "100%", flexGrow: 1}}>
<Table stickyHeader={true} size="small" style={{"tableLayout": "fixed", "maxWidth": "100%", "overflow": "scroll"}}>
<TableHead>
@@ -61,8 +58,8 @@ export function BrowserScriptsTable(props){
))}
</TableBody>
</Table>
</TableContainer>
</div>
</TableContainer>
</>
)
}
@@ -12,6 +12,7 @@ import CardHeader from '@mui/material/CardHeader';
import {gql, useQuery} from '@apollo/client';
import { CardContent } from '@mui/material';
import {classes, StyledButton, StyledDivider} from '../../MythicComponents/MythicTransferList';
import {MythicDraggableDialogTitle} from "../../MythicComponents/MythicDraggableDialogTitle";
const getCommandsQuery = gql`
query GetCallbackDetails($callback_id: Int!) {
@@ -184,8 +185,8 @@ export function AddRemoveCallbackCommandsDialog(props) {
}
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">Add or Remove Commands for Callback {props.display_id} </DialogTitle>
<DialogContent dividers={true} style={{height: "100%", display: "flex", flexDirection: "column", position: "relative", maxHeight: "100%"}}>
<DialogTitle>Add or Remove Commands for Callback {props.display_id} </DialogTitle>
<DialogContent dividers={true} style={{height: "100%", display: "flex", flexDirection: "column", position: "relative", overflowY: "auto"}}>
This will add or remove commands associated with this callback from Mythic's perspective.
This does NOT add or remove commands within the payload itself that's beaconing out to Mythic.
<div style={{display: "flex", flexDirection: "row", overflowY: "auto", flexGrow: 1, minHeight: 0}}>
@@ -316,13 +316,13 @@ function CallbacksTablePreMemo(props){
const ele = document.getElementById("download_config");
if(ele !== null){
ele.href = URL.createObjectURL(dataBlob);
ele.download = data.exportCallbackConfig.agent_callback_id + ".json";
ele.download = "exported_callback.json";
ele.click();
}else{
const element = document.createElement("a");
element.id = "download_config";
element.href = URL.createObjectURL(dataBlob);
element.download = data.exportCallbackConfig.agent_callback_id + ".json";
element.download = "exported_callback.json";
document.body.appendChild(element);
element.click();
}
@@ -197,11 +197,13 @@ export const CallbacksTabsFileBrowserTable = (props) => {
return tempData;
}, [allData, sortData]);
const onSubmitFilterOptions = (value) => {
setFilterOptions({...filterOptions, [selectedColumn.key]: value });
try{
updateSetting({setting_name: `file_browser_filter_options`, value: {...filterOptions,[selectedColumn.key]: value }});
let newValue = value ? value : "";
setFilterOptions({...filterOptions, [selectedColumn.key]: newValue });
updateSetting({setting_name: `file_browser_filter_options`, value: {...filterOptions,[selectedColumn.key]: newValue }});
}catch(error){
console.log("failed to save filter options");
console.log("failed to save filter options", error);
}
}
const filterRow = (row) => {
@@ -502,7 +504,7 @@ export const CallbacksTabsFileBrowserTable = (props) => {
if(removeCommand !== undefined){
removeDisplay = `Remove (${removeCommand.command.cmd})`;
if(element.can_have_children){
downloadDisplay = `Remove Folder (${removeCommand.command.cmd})`;
removeDisplay = `Remove Folder (${removeCommand.command.cmd})`;
}
}
return [
@@ -393,11 +393,12 @@ export const CallbacksTabsProcessBrowserTable = ({treeAdjMatrix, treeRootData, m
return tempData;
}, [allData, sortData]);
const onSubmitFilterOptions = (value) => {
setFilterOptions({...filterOptions, [selectedColumn.current.key]: value });
let newValue = value ? value : "";
try{
updateSetting({setting_name: `process_browser_filter_options`, value: {...filterOptions,[selectedColumn.current.key]: value }});
setFilterOptions({...filterOptions, [selectedColumn.current.key]: newValue });
updateSetting({setting_name: `process_browser_filter_options`, value: {...filterOptions,[selectedColumn.current.key]: newValue }});
}catch(error){
console.log("failed to save filter options");
console.log("failed to save filter options", error, filterOptions, selectedColumn.current.key, newValue);
}
if(viewSingleTreeData){
return
@@ -495,7 +496,7 @@ export const CallbacksTabsProcessBrowserTable = ({treeAdjMatrix, treeRootData, m
}
setSingleTreeData(singleTreeAdjMatrix);
onSubmitFilterOptions({});
onSubmitFilterOptions(undefined);
}
const toggleViewSingleTreeData = () => {
setViewSingleTreeData(!viewSingleTreeData);
@@ -767,7 +768,7 @@ export const CallbacksTabsProcessBrowserTable = ({treeAdjMatrix, treeRootData, m
<MythicModifyStringDialog
title='Filter Column'
onSubmit={onSubmitFilterOptions}
value={filterOptions[selectedColumn.current]}
value={String(filterOptions[selectedColumn.current.key])}
onClose={() => {
setOpenContextMenu(false);
}}
@@ -827,7 +827,7 @@ export function CallbacksTabsTaskingInputPreMemo(props){
buffer += value;
return;
}
else if(value === " "){
else if([" ", "\t", "\r", "\n"].includes(value)){
if(backslash){
backslash = false;
//buffer += " ";
@@ -97,9 +97,9 @@ export const ResponseDisplay = (props) =>{
const interactive = props?.task?.command?.supported_ui_features.includes("task_response:interactive") || false;
return (
interactive ? (
<ResponseDisplayInteractive {...props} />
<ResponseDisplayInteractive {...props} key={props?.task?.id} />
) : (
<NonInteractiveResponseDisplay {...props} />
<NonInteractiveResponseDisplay {...props} key={props?.task?.id} />
)
)
}
@@ -21,6 +21,7 @@ import WrapTextIcon from '@mui/icons-material/WrapText';
import { useReactiveVar } from '@apollo/client';
import { meState } from '../../../cache';
import {useTheme} from '@mui/material/styles';
import HeightIcon from '@mui/icons-material/Height';
const getInteractiveTaskingQuery = gql`
${taskingDataFragment}
@@ -168,7 +169,6 @@ export const GetOutputFormatAll = ({data, myTask, taskID, useASNIColor, message
}
const InteractiveMessageTypes = [
{"name": "None", "value": -1, "text": "None"},
{"name": "Tab", "value": 13, "text": "^I"},
@@ -204,7 +204,7 @@ export const ResponseDisplayInteractive = (props) =>{
const me = useReactiveVar(meState);
const theme = useTheme();
const [backdropOpen, setBackdropOpen] = React.useState(false);
const [scrollToBottom, setScrollToBottom] = React.useState(false);
const [scrollToBottom, setScrollToBottom] = React.useState(true);
const pageSize = React.useRef(100);
const highestFetched = React.useRef(0);
const [taskData, setTaskData] = React.useState([]);
@@ -219,6 +219,7 @@ export const ResponseDisplayInteractive = (props) =>{
const [useASNIColor, setUseANSIColor] = React.useState(true);
const [showTaskStatus, setShowTaskStatus] = React.useState(true);
const [wrapText, setWrapText] = React.useState(true);
const [autoScroll, setAutoScroll] = React.useState(true);
const {loading: loadingTasks} = useSubscription(getInteractiveTaskingQuery, {
variables: {parent_task_id: props.task.id},
onError: data => {
@@ -280,8 +281,7 @@ export const ResponseDisplayInteractive = (props) =>{
if(backdropOpen){
setBackdropOpen(false);
}
}, [highestFetched.current, rawResponses, props.task.id, backdropOpen, taskIDRef.current]);
}, [highestFetched.current, rawResponses, props.task.id, backdropOpen, taskIDRef.current, autoScroll]);
useSubscription(subResponsesQuery, {
variables: {task_id: props.task.id},
fetchPolicy: "no-cache",
@@ -368,11 +368,14 @@ export const ResponseDisplayInteractive = (props) =>{
const toggleWrapText = () => {
setWrapText(!wrapText);
}
useEffect( () => {
if(scrollToBottom){
messagesEndRef.current.scrollIntoView();
const toggleAutoScroll = () => {
setAutoScroll(!autoScroll);
}
useEffect(() => {
if(autoScroll){
messagesEndRef?.current?.scrollIntoView({ behavior: "auto", block: "nearest" });
}
}, [scrollToBottom]);
}, [props.responseRef?.current?.scrollHeight, autoScroll]);
React.useEffect( () => {
if(loadingTasks){
setTaskData([]);
@@ -417,16 +420,14 @@ export const ResponseDisplayInteractive = (props) =>{
<div style={{overflowY: "auto", width: "100%", marginBottom: "5px", height: props.expand ? "100%": undefined,
flexGrow: 1, paddingLeft: "10px", minHeight: "50px"}} ref={props.responseRef}
id={`ptytask${props.task.id}`}>
<GetOutputFormatAll data={alloutput}
myTask={props.task.operator.username === (me?.user?.username || "")}
taskID={props.task.id}
useASNIColor={useASNIColor}
messagesEndRef={messagesEndRef}
showTaskStatus={showTaskStatus}
search={props.searchOutput ? search : undefined}
wrapText={wrapText}/>
<GetOutputFormatAll data={alloutput}
myTask={props.task.operator.username === (me?.user?.username || "")}
taskID={props.task.id}
useASNIColor={useASNIColor}
messagesEndRef={messagesEndRef}
showTaskStatus={showTaskStatus}
search={props.searchOutput ? search : undefined}
wrapText={wrapText}/>
<div ref={messagesEndRef}/>
</div>
{!props.task?.is_interactive_task &&
@@ -435,6 +436,7 @@ export const ResponseDisplayInteractive = (props) =>{
useASNIColor={useASNIColor} toggleANSIColor={toggleANSIColor}
showTaskStatus={showTaskStatus} toggleShowTaskStatus={toggleShowTaskStatus}
wrapText={wrapText} toggleWrapText={toggleWrapText}
autoScroll={autoScroll} toggleAutoScroll={toggleAutoScroll}
/>
</div>
}
@@ -448,7 +450,8 @@ export const ResponseDisplayInteractive = (props) =>{
}
const InteractiveTaskingBar = ({
task, taskData, useASNIColor, toggleANSIColor,
showTaskStatus, toggleShowTaskStatus, wrapText, toggleWrapText
showTaskStatus, toggleShowTaskStatus, wrapText, toggleWrapText,
autoScroll, toggleAutoScroll
}) => {
const [inputText, setInputText] = React.useState("");
const theme = useTheme();
@@ -465,8 +468,8 @@ const InteractiveTaskingBar = ({
});
const [taskOptionsIndex, setTaskOptionsIndex] = React.useState(-1);
const [taskOptions, setTaskOptions] = React.useState([]);
const [selectedEnterOption, setSelectedEnterOption] = React.useState(EnterOptions[1]);
const [selectedControlOption, setSelectedControlOption] = React.useState(InteractiveMessageTypes[0]);
const [selectedEnterOption, setSelectedEnterOption] = React.useState(1);
const [selectedControlOption, setSelectedControlOption] = React.useState(0);
React.useEffect( () => {
const newTaskOptions = taskData.filter( t => t.display_params.length > 1 && (t.interactive_task_type === 0 || t.interactive_task_type === 8));
newTaskOptions.sort( (a,b) => a.id > b.id ? -1 : 1);
@@ -516,23 +519,23 @@ const InteractiveTaskingBar = ({
event.stopPropagation();
event.preventDefault();
if(event.shiftKey){
setInputText(inputText + selectedEnterOption.value);
setInputText(inputText + EnterOptions[selectedEnterOption].value);
return;
}
if(event.metaKey || event.ctrlKey){
setInputText(inputText + selectedEnterOption.value);
setInputText(inputText + EnterOptions[selectedEnterOption].value);
return;
}
if(selectedControlOption.value > 0){
let ctrlSequence = selectedControlOption.text;
let enterOption = selectedEnterOption.value;
if(InteractiveMessageTypes[selectedControlOption].value > 0){
let ctrlSequence = InteractiveMessageTypes[selectedControlOption].text;
let enterOption = EnterOptions[selectedEnterOption].value;
let originalParams = inputText + ctrlSequence + enterOption;
// if we're looking at a tab, never send enter along with it
if (selectedControlOption.value === 8){
if (InteractiveMessageTypes[selectedControlOption].value === 8){
originalParams = inputText + ctrlSequence;
enterOption = "";
// if we're looking at escape, never send enter along with it
} else if(selectedControlOption.value === 4){
} else if(InteractiveMessageTypes[selectedControlOption].value === 4){
originalParams = ctrlSequence + inputText;
enterOption = "";
}
@@ -545,16 +548,16 @@ const InteractiveTaskingBar = ({
parameter_group_name: "default",
parent_task_id: task.id,
is_interactive_task: true,
interactive_task_type: selectedControlOption.value,
interactive_task_type: InteractiveMessageTypes[selectedControlOption].value,
}})
}else {
// no control option selected, just send data along as input
createTask({variables: {
callback_id: task.callback.display_id,
command: task.command.cmd,
params: inputText + selectedEnterOption.value,
params: inputText + EnterOptions[selectedEnterOption].value,
tasking_location: "command_line",
original_params: inputText + selectedEnterOption.value,
original_params: inputText + EnterOptions[selectedEnterOption].value,
parameter_group_name: "default",
parent_task_id: task.id,
is_interactive_task: true,
@@ -562,7 +565,7 @@ const InteractiveTaskingBar = ({
}})
}
setInputText("");
setSelectedControlOption(InteractiveMessageTypes[0]);
setSelectedControlOption(0);
setTaskOptionsIndex(-1);
}
const onChangeSelect = (event) => {
@@ -600,8 +603,8 @@ const InteractiveTaskingBar = ({
}}
input={<Input style={{margin: 0, color: theme.outputTextColor}} />}
>
{InteractiveMessageTypes.map( (opt) => (
<MenuItem value={opt} key={opt.name}>{opt.name}</MenuItem>
{InteractiveMessageTypes.map( (opt,index) => (
<MenuItem value={index} key={opt.name}>{opt.name}</MenuItem>
) )}
</Select>
</FormControl>
@@ -626,8 +629,8 @@ const InteractiveTaskingBar = ({
input={<Input style={{color: theme.outputTextColor}} />}
IconComponent={KeyboardReturnIcon}
>
{EnterOptions.map( (opt) => (
<MenuItem value={opt} key={opt.name}>{opt.name}</MenuItem>
{EnterOptions.map( (opt,index) => (
<MenuItem value={index} key={opt.name}>{opt.name}</MenuItem>
) )}
</Select>
</FormControl>
@@ -637,7 +640,6 @@ const InteractiveTaskingBar = ({
style={{cursor: "pointer"}}
/>
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={showTaskStatus ? "Hide Task Status" : "Show Task Status"} >
<IconButton onClick={toggleShowTaskStatus} style={{paddingLeft: 0, paddingRight: 0}} disableRipple={true} disableFocusRipple={true}>
@@ -645,7 +647,6 @@ const InteractiveTaskingBar = ({
style={{cursor: "pointer",}}
/>
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={wrapText ? "Unwrap Text" : "Wrap Text"} >
<IconButton onClick={toggleWrapText} style={{paddingLeft: 0, paddingRight: 0}} disableRipple={true} disableFocusRipple={true}>
@@ -653,7 +654,13 @@ const InteractiveTaskingBar = ({
style={{cursor: "pointer"}}
/>
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={autoScroll ? "Stop Auto Scroll" : "Auto Scroll"} >
<IconButton onClick={toggleAutoScroll} style={{paddingLeft: 0, paddingRight: 0}} disableRipple={true} disableFocusRipple={true}>
<HeightIcon color={autoScroll ? "success" : "secondary"}
style={{cursor: "pointer"}}
/>
</IconButton>
</MythicStyledTooltip>
</div>
)
@@ -69,9 +69,10 @@ export function ResponseDisplayTabs({ tabs, task, expand, displayType, output })
return (
<div style={{width: "100%", maxWidth: "100%", display: 'flex', flexDirection: 'column', flexGrow: 1, height: expand ? "100%" : "400px", }}>
<AppBar color='default' position={"static"} style={{}} >
<AppBar color='default' position={"static"} style={{maxHeight: "25%", overflowY: 'auto', maxWidth: "100%", flexWrap: "no-wrap"}} >
<Tabs
value={value}
variant={"standard"}
onChange={handleChange}
indicatorColor='primary'
textColor='primary'
@@ -288,41 +288,36 @@ const SideDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput,
}
return (
<div style={{height: "100%"}}>
{openTaskTagDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openTaskTagDialog}
{openTaskTagDialog && <MythicDialog fullWidth={true} maxWidth="lg" open={openTaskTagDialog}
onClose={()=>{setOpenTaskTagDialog(false);}}
innerDialog={<ViewEditTagsDialog me={me} target_object={"task_id"} target_object_id={task.id} onClose={()=>{setOpenTaskTagDialog(false);}} />}
/>) : null}
{openCommentDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openCommentDialog}
/>
}
{openCommentDialog && <MythicDialog fullWidth={true} maxWidth="md" open={openCommentDialog}
onClose={()=>{setOpenCommentDialog(false);}}
innerDialog={<TaskCommentDialog task_id={task.id} onClose={()=>{setOpenCommentDialog(false);}} />}
/>) : null
/>
}
{openParametersDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openParametersDialog}
{openParametersDialog && <MythicDialog fullWidth={true} maxWidth="lg" open={openParametersDialog}
onClose={()=>{setOpenParametersDialog(false);}}
innerDialog={<TaskViewParametersDialog task_id={task.id} onClose={()=>{setOpenParametersDialog(false);}} />}
/>) : null
/>
}
{openTokenDialog ?
(<MythicDialog fullWidth={true} maxWidth="md" open={openTokenDialog}
{openTokenDialog && <MythicDialog fullWidth={true} maxWidth="md" open={openTokenDialog}
onClose={()=>{setOpenTokenDialog(false);}}
innerDialog={<TaskTokenDialog token_id={task.token === undefined ? 0 : task.token.id} onClose={()=>{setOpenTokenDialog(false);}} />}
/>) : null
/>
}
{openOpsecDialog.open ?
(<MythicDialog fullWidth={true} maxWidth="md" open={openOpsecDialog.open}
{openOpsecDialog.open && <MythicDialog fullWidth={true} maxWidth="lg" open={openOpsecDialog.open}
onClose={()=>{setOpenOpsecDialog({...openOpsecDialog, open: false});}}
innerDialog={<TaskOpsecDialog task_id={task.id} view={openOpsecDialog.view} onClose={()=>{setOpenOpsecDialog({...openOpsecDialog, open: false});}} />}
/>) : null
/>
}
{openStdoutStderrDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openStdoutStderrDialog}
{openStdoutStderrDialog && <MythicDialog fullWidth={true} maxWidth="lg" open={openStdoutStderrDialog}
onClose={()=>{setOpenStdoutStderrDialog(false);}}
innerDialog={<TaskViewStdoutStderrDialog task_id={task.id} onClose={()=>{setOpenStdoutStderrDialog(false);}} />}
/>) : null
/>
}
{openEventingDialog &&
<MythicDialog
@@ -562,41 +557,36 @@ const SpeedDialDisplayGeneric = ({toggleViewBrowserScript, toggleSelectAllOutput
return (
<React.Fragment>
<Backdrop open={openSpeedDial} onClick={()=>{setOpenSpeedDial(false);}} style={{zIndex: 2, position: "absolute"}}/>
{openTaskTagDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openTaskTagDialog}
{openTaskTagDialog && <MythicDialog fullWidth={true} maxWidth="lg" open={openTaskTagDialog}
onClose={()=>{setOpenTaskTagDialog(false);}}
innerDialog={<ViewEditTagsDialog me={me} target_object={"task_id"} target_object_id={task.id} onClose={()=>{setOpenTaskTagDialog(false);}} />}
/>) : null}
{openCommentDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openCommentDialog}
/>
}
{openCommentDialog && <MythicDialog fullWidth={true} maxWidth="md" open={openCommentDialog}
onClose={()=>{setOpenCommentDialog(false);}}
innerDialog={<TaskCommentDialog task_id={task.id} onClose={()=>{setOpenCommentDialog(false);}} />}
/>) : null
/>
}
{openParametersDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openParametersDialog}
{openParametersDialog && <MythicDialog fullWidth={true} maxWidth="lg" open={openParametersDialog}
onClose={()=>{setOpenParametersDialog(false);}}
innerDialog={<TaskViewParametersDialog task_id={task.id} onClose={()=>{setOpenParametersDialog(false);}} />}
/>) : null
/>
}
{openTokenDialog ?
(<MythicDialog fullWidth={true} maxWidth="md" open={openTokenDialog}
{openTokenDialog && <MythicDialog fullWidth={true} maxWidth="md" open={openTokenDialog}
onClose={()=>{setOpenTokenDialog(false);}}
innerDialog={<TaskTokenDialog token_id={task.token === undefined ? 0 : task.token.id} onClose={()=>{setOpenTokenDialog(false);}} />}
/>) : null
/>
}
{openOpsecDialog.open ?
(<MythicDialog fullWidth={true} maxWidth="md" open={openOpsecDialog.open}
{openOpsecDialog.open && <MythicDialog fullWidth={true} maxWidth="lg" open={openOpsecDialog.open}
onClose={()=>{setOpenOpsecDialog({...openOpsecDialog, open: false});}}
innerDialog={<TaskOpsecDialog task_id={task.id} view={openOpsecDialog.view} onClose={()=>{setOpenOpsecDialog({...openOpsecDialog, open: false});}} />}
/>) : null
/>
}
{openStdoutStderrDialog ?
(<MythicDialog fullWidth={true} maxWidth="lg" open={openStdoutStderrDialog}
{openStdoutStderrDialog && <MythicDialog fullWidth={true} maxWidth="lg" open={openStdoutStderrDialog}
onClose={()=>{setOpenStdoutStderrDialog(false);}}
innerDialog={<TaskViewStdoutStderrDialog task_id={task.id} onClose={()=>{setOpenStdoutStderrDialog(false);}} />}
/>) : null
/>
}
{openEventingDialog &&
<MythicDialog
@@ -21,6 +21,7 @@ import {Backdrop, CircularProgress} from '@mui/material';
import Divider from '@mui/material/Divider';
import {b64DecodeUnicode} from './ResponseDisplay';
import {snackActions} from "../../utilities/Snackbar";
import {MythicDraggableDialogTitle} from "../../MythicComponents/MythicDraggableDialogTitle";
//if we need to get all the loaded commands for the callback and filter, use this
const GetLoadedCommandsQuery = gql`
@@ -977,7 +978,7 @@ export function TaskParametersDialog(props) {
}
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">{commandInfo.cmd}'s Parameters</DialogTitle>
<MythicDraggableDialogTitle>{commandInfo.cmd}'s Parameters</MythicDraggableDialogTitle>
<DialogContent dividers={true}>
<Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute"}}>
<CircularProgress color="inherit" />
@@ -27,6 +27,7 @@ import MythicStyledTableCell from '../../MythicComponents/MythicTableCell';
import {MythicFileContext} from "../../MythicComponents/MythicFileContext";
import RefreshIcon from '@mui/icons-material/Refresh';
import {updateCredentialDeleted} from "../Search/CredentialTable";
import CloudUploadTwoToneIcon from '@mui/icons-material/CloudUploadTwoTone';
export const getDynamicQueryParamsString = `
mutation getDynamicParamsMutation($callback: Int!, $command: String!, $payload_type: String!, $parameter_name: String!, $other_parameters: jsonb){
@@ -559,13 +560,13 @@ export function TaskParametersDialogRow(props){
setValue(event.target.checked);
props.onChange(props.name, event.target.checked);
}
const onFileChange = (evt) => {
setFileValue({name: evt.target.files[0].name});
props.onChange(props.name, evt.target.files[0]);
const onFileChange = (newFile) => {
setFileValue({name: newFile.name});
props.onChange(props.name, newFile);
}
const onFileMultChange = (evt) => {
setFileMultValue([...evt.target.files]);
props.onChange(props.name, [...evt.target.files]);
const onFileMultChange = (newFiles) => {
setFileMultValue([...newFiles]);
props.onChange(props.name, [...newFiles]);
}
const onChangeAgentConnectHost = (event) => {
setAgentConnectHost(event.target.value);
@@ -923,28 +924,14 @@ export function TaskParametersDialogRow(props){
case "File":
return (
<>
<Button variant="contained" component="label">
{ fileValue.name === "" ? "Select File" : fileValue.name }
<input onChange={onFileChange} type="file" hidden />
</Button>
<DragAndDropFileUpload value={fileValue} multiple={false} onChange={onFileChange} />
</>
)
case "FileMultiple":
return (
<>
<Button variant="contained" component="label">
Select Files
<input onChange={onFileMultChange} type="file" hidden multiple />
</Button>
{ fileMultValue.length > 0 &&
fileMultValue.map((f, i) => (
<div key={i}>
{typeof f === "string" && <MythicFileContext agent_file_id={f} />}
{typeof f !== "string" && (f.name)}
</div>
))
}
<DragAndDropFileUpload values={fileMultValue} multiple={true} onChange={onFileMultChange} />
</>
)
case "LinkInfo":
@@ -1219,3 +1206,115 @@ export function TaskParametersDialogRow(props){
)
}
export const DragAndDropFileUpload = ({value, values, multiple, onChange}) => {
const theme = useTheme();
const inputRef = React.useRef(null);
const [files, setFiles] = React.useState(values ? values : []);
const [file, setFile] = React.useState(value ? value : {name: ""});
const [isDragging, setIsDragging] = React.useState(false);
React.useEffect( () => {
if(value){
setFile(value);
}
if(values){
setFiles(values);
}
}, [value, values]);
const handleDragEnter = () => setIsDragging(true);
const handleDragLeave = (event) => {
if (event.currentTarget.contains(event.relatedTarget)) {
return; // Don't treat as a real leave event
}
setIsDragging(false);
}
const handleDrop = (e) => {
e.preventDefault();
setIsDragging(false);
const droppedFiles = Array.from(e.dataTransfer.files);
if(multiple){
setFiles(droppedFiles);
onChange(droppedFiles)
} else {
setFile(droppedFiles[0]);
onChange(droppedFiles[0]);
}
};
const handleDragOver = (e) => {
e.preventDefault();
};
const onFileChange = (evt) => {
setFile({name: evt.target.files[0].name});
onChange(evt.target.files[0]);
}
const onFileMultChange = (evt) => {
setFiles([...evt.target.files]);
onChange(evt.target.files)
}
const onClick = (e) => {
inputRef.current.click();
}
return (
<div
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onClick={onClick}
style={{
border: isDragging ? `4px dashed ${theme.palette.success.main}` : `4px dashed ${theme.palette.primary.main}`,
padding: "20px",
textAlign: "center",
borderRadius: "10px",
cursor: "pointer"
//backgroundColor: isDragging ? "#f0f8ff" : "#fff",
}}
>
<input ref={inputRef} onChange={multiple ? onFileMultChange : onFileChange} type="file" hidden multiple={multiple} />
<div style={{display: "flex", flexDirection: "column", alignItems: "center"}}>
{!multiple && file.name !== "" &&
<>
<CloudUploadTwoToneIcon fontSize={"large"} color={"success"} />
<Typography>
Selected:
</Typography>
<Typography>
{!file.legacy && file.name}
{file.legacy &&
<MythicFileContext agent_file_id={file.name}
extraStyles={{ position: "relative", marginLeft: "5px", marginRight: "5px"}} />
}
</Typography>
</>
}
{multiple && files.length > 0 &&
<>
<CloudUploadTwoToneIcon fontSize={"large"} color={"success"} />
<Typography>
Selected:
</Typography>
{files.map( (f, i) => (
<div key={i}>
{typeof f === "string" && <MythicFileContext agent_file_id={f} />}
{typeof f !== "string" && (f.name)}
</div>
))}
</>
}
{file.name === "" && files.length === 0 &&
<>
<CloudUploadTwoToneIcon fontSize={"large"} color={"success"} />
<Typography>
Drag and drop files here
</Typography>
<Typography>
Click to open dialog
</Typography>
</>
}
</div>
</div>
);
}
@@ -10,7 +10,7 @@ import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import {MythicDraggableDialogTitle} from "../../MythicComponents/MythicDraggableDialogTitle";
export const allTokenDataFragment = gql`
fragment allTokenData on token {
@@ -89,7 +89,7 @@ export function TaskTokenDialog(props) {
});
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">Token Information</DialogTitle>
<MythicDraggableDialogTitle >Token Information</MythicDraggableDialogTitle>
<TableContainer className="mythicElement">
<Table size="small" style={{"tableLayout": "fixed", "maxWidth": "calc(100vw)", "overflow": "scroll"}}>
<TableHead>
@@ -68,13 +68,14 @@ export function TaskViewParametersDialog(props) {
return <div>Error!</div>;
}
return (
<React.Fragment>
<MythicModifyStringDialog title={`View Task Parameters And Timestamps`}
onClose={props.onClose}
maxRows={40}
wrap={true}
value={comment} />
</React.Fragment>
<>
<MythicModifyStringDialog title={`View Task Parameters And Timestamps`}
onClose={props.onClose}
maxRows={40}
wrap={true}
value={comment} />
</>
);
}
@@ -25,6 +25,7 @@ import { Backdrop } from '@mui/material';
import {CircularProgress} from '@mui/material';
import RefreshIcon from '@mui/icons-material/Refresh';
import InputLabel from '@mui/material/InputLabel';
import {DragAndDropFileUpload} from "../Callbacks/TaskParametersDialogRow";
export const getDynamicQueryBuildParameterParams = gql`
mutation getDynamicBuildParamsMutation($payload_type: String!, $parameter_name: String!, $selected_os: String!){
@@ -182,13 +183,13 @@ export function CreatePayloadParameter({onChange, parameter_type, default_value,
const submitDictChange = (list) => {
onChange(name, list, false);
};
const onFileChange = (evt) => {
setFileValue({name: evt.target.files[0].name});
onChange(name, evt.target.files[0]);
const onFileChange = (newFile) => {
setFileValue({name: newFile.name});
onChange(name, newFile);
}
const onFileMultChange = (evt) => {
setFileMultValue([...evt.target.files]);
onChange(name, [...evt.target.files]);
const onFileMultChange = (newFiles) => {
setFileMultValue([...newFiles]);
onChange(name, [...newFiles]);
}
useEffect( () => {
if( parameter_type === "ChooseOne" || parameter_type === "ChooseOneCustom" ){
@@ -474,32 +475,13 @@ export function CreatePayloadParameter({onChange, parameter_type, default_value,
case "FileMultiple":
return (
<>
<Button variant="contained" component="label">
Select Files
<input onChange={onFileMultChange} type="file" hidden multiple />
</Button>
{ fileMultValue.length > 0 &&
fileMultValue?.map((f, i) => (
<div key={i} style={{display: "inline-block"}}>
{typeof f === "string" && <MythicFileContext agent_file_id={f}
extraStyles={{bottom: "-10px", position: "relative", marginLeft: "5px", marginRight: "5px"}} />}
{typeof f !== "string" && (f.name)}
</div>
))
}
<DragAndDropFileUpload values={fileMultValue} multiple={true} onChange={onFileMultChange} />
</>
)
case "File":
return (
<>
<Button variant="contained" component="label">
{ fileValue.legacy ? "Select New File" : fileValue.name === "" ? "Select File" : fileValue.name }
<input onChange={onFileChange} type="file" hidden />
</Button>
{fileValue.legacy &&
<MythicFileContext agent_file_id={fileValue.name}
extraStyles={{ position: "relative", marginLeft: "5px", marginRight: "5px"}} />
}
<DragAndDropFileUpload value={fileValue} multiple={false} onChange={onFileChange} />
</>
)
@@ -39,6 +39,7 @@ query getPayloadTypesC2ProfilesQuery($payloadType: String!, $operation_id: Int!)
required
verifier_regex
choices
ui_position
}
c2profileparametersinstances(where: {instance_name: {_is_null: false}, operation_id: {_eq: $operation_id}}, distinct_on: instance_name, order_by: {instance_name: asc}){
instance_name
@@ -61,6 +62,7 @@ query getProfileInstanceQuery($name: String!, $operation_id: Int!, $c2_profile_i
required
verifier_regex
choices
ui_position
c2profile {
name
}
@@ -86,6 +88,7 @@ query getDefaultC2ProfileParameters($c2profile_id: Int!) {
required
verifier_regex
choices
ui_position
}
}
}
@@ -150,7 +153,13 @@ export function Step4C2Profiles(props){
onCompleted: data => {
const profiles = data.c2profile.map( (c2) => {
const parameters = getModifiedC2Params(c2, c2.c2profileparameters, props.buildOptions, false);
parameters.sort((a,b) => -b.description.localeCompare(a.description));
let hasUiPositions = parameters.filter(p => p.ui_position > 0).length > 0;
if(hasUiPositions){
parameters.sort((a,b) => a.ui_position > b.ui_position ? 1 : a.ui_position === b.ui_position ? 0 : -1);
} else {
parameters.sort((a,b) => -b.description.localeCompare(a.description));
}
return {...c2, c2profileparameters: parameters, "selected_instance": "None"};
});
profiles.sort((a, b) => -b.name.localeCompare(a.name))
@@ -285,7 +294,13 @@ export function Step4C2Profiles(props){
return inst;
});
updates = getModifiedC2Params(c2, updates, props.buildOptions, true);
updates.sort( (a, b) => a.description < b.description ? -1 : 1);
//updates.sort( (a, b) => a.description < b.description ? -1 : 1);
let hasUiPositions = updates.filter(p => p.ui_position > 0).length > 0;
if(hasUiPositions){
updates.sort((a,b) => a.ui_position > b.ui_position ? 1 : a.ui_position === b.ui_position ? 0 : -1);
} else {
updates.sort((a,b) => -b.description.localeCompare(a.description));
}
const updatedc2 = includedC2Profiles.map( (curc2, indx) => {
if(index === indx){
return {...curc2, c2profileparameters: updates, selected_instance: evt.target.value};
@@ -310,7 +325,13 @@ export function Step4C2Profiles(props){
choices: getDefaultChoices(param)};
});
updates = getModifiedC2Params(c2, updates, props.buildOptions, false);
updates.sort( (a, b) => a.description < b.description ? -1 : 1);
let hasUiPositions = updates.filter(p => p.ui_position > 0).length > 0;
if(hasUiPositions){
updates.sort((a,b) => a.ui_position > b.ui_position ? 1 : a.ui_position === b.ui_position ? 0 : -1);
} else {
updates.sort((a,b) => -b.description.localeCompare(a.description));
}
//updates.sort( (a, b) => a.description < b.description ? -1 : 1);
const updatedc2 = includedC2Profiles.map( (curc2, indx) => {
if(index === indx){
return {...curc2, c2profileparameters: updates, selected_instance: 'None'};
@@ -81,6 +81,8 @@ export function Step5Build(props){
} else if (param.parameter_type === "File") {
if (typeof param.value === "string") {
buildParameters.push({name: param.name, value: param.value});
}else if(param.value.legacy){
buildParameters.push({name: param.name, value: param.value.name});
} else {
const newUUID = await UploadTaskFile(param.value, "Uploaded as build parameter for " + filename);
if (newUUID) {
@@ -97,6 +99,8 @@ export function Step5Build(props){
for(let j = 0; j < param.value.length; j++){
if (typeof param.value[j] === "string") {
fileMultipleValues.push(param.value[j]);
}else if(param.value[j].legacy){
fileMultipleValues.push(param.value[j].name);
} else {
const newUUID = await UploadTaskFile(param.value[j], "Uploaded as build parameter for " + filename);
if (newUUID) {
@@ -1,174 +0,0 @@
import React, { } from 'react';
import {useQuery, gql} from '@apollo/client';
import CircularProgress from '@mui/material/CircularProgress';
import Select from '@mui/material/Select';
import { CreatePayloadNavigationButtons} from './CreatePayloadNavigationButtons';
import {CreatePayloadBuildParametersTable} from './CreatePayloadBuildParametersTable';
import {snackActions} from '../../utilities/Snackbar';
import Typography from '@mui/material/Typography';
import {getDefaultValueForType, getDefaultChoices} from '../CreatePayload/Step2SelectPayloadType';
import MenuItem from '@mui/material/MenuItem';
const GET_Payload_Types = gql`
query getPayloadTypesBuildParametersQuery($os: jsonb!) {
payloadtype(where: {supported_os: {_contains: $os}, deleted: {_eq: false}, wrapper: {_eq: true}}, order_by: {name: asc}) {
name
id
file_extension
supports_dynamic_loading
buildparameters(where: {deleted: {_eq: false} }, order_by: {description: asc}) {
default_value
description
format_string
id
name
parameter_type
randomize
required
verifier_regex
choices
}
}
}
`;
export function Step2SelectPayloadType(props){
const [selectedPayloadType, setSelectedPayloadType] = React.useState('');
const [selectedPayloadTypeID, setSelectedPayloadTypeID] = React.useState(0);
const [fileExtension, setFileExtension] = React.useState('');
const [payloadTypeParameters, setSelectedPayloadTypeParameters] = React.useState([]);
const { loading, error, data } = useQuery(GET_Payload_Types,
{variables:{os: props.buildOptions},
fetchPolicy: "no-cache",
onCompleted: data => {
if(data.payloadtype.length > 0){
if(props.prevData !== undefined && props.prevData.os === props.buildOptions){
setSelectedPayloadType(props.prevData.payload_type);
setSelectedPayloadTypeID(props.prevData.payload_type_id);
setFileExtension(props.prevData.file_extension);
const payloadtypedata = data.payloadtype.reduce( (prev, payload) => {
if(payload.name === props.prevData.payload_type){
const params = payload.buildparameters.map( (param) => {
for(let p = 0; p < props.prevData.parameters.length; p++){
if(props.prevData.parameters[p]["name"] === param.name){
return {...param, error: false,
value: props.prevData.parameters[p]["value"],
trackedValue: props.prevData.parameters[p]["value"],
initialValue: getDefaultValueForType(param),
choices: getDefaultChoices(param)
}
}
}
});
return [...prev, ...params];
}
return [...prev];
}, []);
payloadtypedata.sort((a,b) => -b.description.localeCompare(a.description));
setSelectedPayloadTypeParameters(payloadtypedata);
if(payloadtypedata.length === 0){
snackActions.warning("No available payload types exist for the selected OS");
}
}else{
setSelectedPayloadType(data.payloadtype[0].name);
setSelectedPayloadTypeID(data.payloadtype[0].id);
setFileExtension(data.payloadtype[0].file_extension);
const payloadtypedata = data.payloadtype.reduce( (prev, payload) => {
if(payload.name === data.payloadtype[0].name){
const params = payload.buildparameters.map( (param) => {
const initialValue = getDefaultValueForType(param);
return {...param, error: false, value: initialValue,
trackedValue: initialValue,
initialValue: initialValue,
choices: getDefaultChoices(param)}
});
return [...prev, ...params];
}
return [...prev];
}, []);
payloadtypedata.sort((a,b) => -b.description.localeCompare(a.description));
setSelectedPayloadTypeParameters(payloadtypedata);
}
}
}
});
const finished = () => {
if(selectedPayloadType === ""){
snackActions.warning("No payload type selected");
return;
}
props.finished({"payload_type": selectedPayloadType,
"parameters": payloadTypeParameters,
"payload_type_id": selectedPayloadTypeID,
"file_extension": fileExtension,
"os": props.buildOptions});
}
const canceled = () => {
props.canceled();
}
const changePayloadType = (evt) => {
setSelectedPayloadType(evt.target.value);
const payloadtypedata = data.payloadtype.reduce( (prev, payload) => {
if(payload.name === evt.target.value){
setFileExtension(payload.file_extension);
setSelectedPayloadTypeID(payload.id);
const params = payload.buildparameters.map( (param) => {
const initialValue = getDefaultValueForType(param);
return {...param, error: false, value: initialValue,
trackedValue: initialValue,
initialValue: initialValue,
choices: getDefaultChoices(param)}
});
return [...prev, ...params];
}
return [...prev];
}, []);
payloadtypedata.sort((a,b) => -b.description.localeCompare(a.description));
setSelectedPayloadTypeParameters(payloadtypedata);
}
const onChange = (name, value, error) => {
const newParams = payloadTypeParameters.map( (param) => {
if(param.name === name){
return {...param, value, error}
}
return {...param};
});
setSelectedPayloadTypeParameters(newParams);
}
if (loading) {
return <div><CircularProgress /></div>;
}
if (error) {
console.error(error);
return <div>Error! {error.message}</div>;
}
return (
<div style={{height: "100%", display: "flex", flexDirection: "column"}}>
<Typography variant="h3" align="left" id="selectospage" component="div"
style={{"marginLeft": "10px"}}>
Select Target Payload Type
</Typography>
<Select
value={selectedPayloadType}
onChange={changePayloadType}
>
{
data.payloadtype.map((opt) => (
<MenuItem key={"step2" + opt.name} value={opt.name}>{opt.name}</MenuItem>
))
}
</Select><br/>
<div style={{display: "flex", flexGrow: 1, overflowY: "auto"}}>
<CreatePayloadBuildParametersTable onChange={onChange} buildParameters={payloadTypeParameters}/>
</div>
<CreatePayloadNavigationButtons first={props.first} last={props.last} canceled={canceled}
finished={finished}/>
<br/><br/>
</div>
);
}
@@ -103,6 +103,8 @@ export function Step5Build(props){
} else if (param.parameter_type === "File") {
if (typeof param.value === "string") {
buildParameters.push({name: param.name, value: param.value});
} else if(param.value.legacy){
buildParameters.push({name: param.name, value: param.value.name});
} else {
const newUUID = await UploadTaskFile(param.value, "Uploaded as build parameter for " + filename);
if (newUUID) {
@@ -119,6 +121,8 @@ export function Step5Build(props){
for(let j = 0; j < param.value.length; j++){
if (typeof param.value[j] === "string") {
fileMultipleValues.push(param.value[j]);
}else if(param.value[j].legacy){
fileMultipleValues.push(param.value[j].name);
} else {
const newUUID = await UploadTaskFile(param.value[j], "Uploaded as build parameter for " + filename);
if (newUUID) {
@@ -15,6 +15,8 @@ import Select from '@mui/material/Select';
import Grid from '@mui/material/Grid';
import {alertCount} from "../../../cache";
import {levelOptions} from "./EventFeed";
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
const EventList = ({onUpdateLevel, onUpdateResolution, operationeventlog}) => {
return (
@@ -51,42 +53,41 @@ export function EventFeedTable(props){
}
}, []);
return (
<div style={{display: "flex", flexDirection: "column", height: "100%", maxWidth: "100%", overflowX: "hidden"}}>
<Paper elevation={5} style={{backgroundColor: theme.body, marginBottom: "5px", marginRight: "5px"}}>
<Grid container spacing={1} style={{ marginTop: "0px"}}>
<Grid style={{paddingTop: 0}} size={10}>
<MythicTextField placeholder="Search..." value={search} marginBottom={"0px"} marginTop={"0px"}
onChange={handleSearchValueChange} onEnter={submitSearch} InputProps={{
endAdornment:
<React.Fragment>
<Tooltip title="Search">
<IconButton onClick={submitSearch} size="large"><SearchIcon style={{color: theme.palette.info.main}}/></IconButton>
</Tooltip>
<Tooltip title="Resolve Viewable Errors">
<IconButton onClick={props.resolveViewableErrors} size="large"><AutoFixHighIcon style={{color: theme.palette.success.main}}/></IconButton>
</Tooltip>
<Tooltip title="Resolve All Errors">
<IconButton onClick={props.resolveAllErrors} size="large"><HealingIcon style={{color: theme.palette.success.main}}/></IconButton>
</Tooltip>
</React.Fragment>,
style: {padding: 0}
}}/>
</Grid>
<Grid style={{paddingTop: 0, paddingLeft: 0}} size={2}>
<Select
style={{width: "100%"}}
value={level}
onChange={handleLevelValueChange}
>
{
levelOptions.map((opt, i) => (
<MenuItem key={"levelFilter" + opt} value={opt}>{opt}</MenuItem>
))
}
</Select>
</Grid>
<MythicPageBody>
<Grid container spacing={1} style={{ marginTop: "0px"}}>
<Grid style={{paddingTop: 0}} size={10}>
<MythicTextField placeholder="Search..." value={search} marginBottom={"0px"} marginTop={"0px"}
onChange={handleSearchValueChange} onEnter={submitSearch} InputProps={{
endAdornment:
<React.Fragment>
<Tooltip title="Search">
<IconButton onClick={submitSearch} size="large"><SearchIcon style={{color: theme.palette.info.main}}/></IconButton>
</Tooltip>
<Tooltip title="Resolve Viewable Errors">
<IconButton onClick={props.resolveViewableErrors} size="large"><AutoFixHighIcon style={{color: theme.palette.success.main}}/></IconButton>
</Tooltip>
<Tooltip title="Resolve All Errors">
<IconButton onClick={props.resolveAllErrors} size="large"><HealingIcon style={{color: theme.palette.success.main}}/></IconButton>
</Tooltip>
</React.Fragment>,
style: {padding: 0}
}}/>
</Grid>
</Paper>
<Grid style={{paddingTop: 0, paddingLeft: 0}} size={2}>
<Select
style={{width: "100%"}}
value={level}
onChange={handleLevelValueChange}
>
{
levelOptions.map((opt, i) => (
<MenuItem key={"levelFilter" + opt} value={opt}>{opt}</MenuItem>
))
}
</Select>
</Grid>
</Grid>
<div style={{display: "flex", flexDirection: "column", overflowY: "auto", flexGrow: 1, overflowX: "Hidden"}}>
<EventList
onUpdateResolution={props.onUpdateResolution}
@@ -98,6 +99,6 @@ export function EventFeedTable(props){
siblingCount={1} onChange={props.onChangePage} showFirstButton={true} showLastButton={true} style={{padding: "20px"}}/>
<Typography style={{paddingLeft: "10px"}}>Total Results: {props.pageData.totalCount}</Typography>
</div>
</div>
</MythicPageBody>
);
}
@@ -66,7 +66,7 @@ mutation eventingManualTrigger($eventgroup_id: Int!){
}
}
`)
export function EventGroupTable({selectedEventGroup, me, showInstances, showGraph}) {
export function EventGroupTable({selectedEventGroup, me, showInstances, showGraph, height}) {
const [openEventStepRender, setOpenEventStepRender] = React.useState(false);
const [openEnvView, setOpenEnvView] = React.useState(false);
const [openTriggerDataView, setOpenTriggerDataView] = React.useState(false);
@@ -141,7 +141,7 @@ export function EventGroupTable({selectedEventGroup, me, showInstances, showGrap
triggerManually({variables: {eventgroup_id: selectedEventGroup.id}});
}
return (
<div style={{marginLeft: "5px", display: "flex", overflowY: "auto", flexDirection: "column", height: "100%"}}>
<div style={{marginLeft: "5px", display: "flex", overflowY: "auto", flexDirection: "column", height: height || "100%"}}>
{selectedEventGroup.id === 0 &&
<Typography variant={"h4"}><strong>All Eventing Runs</strong></Typography>
@@ -19,6 +19,7 @@ import {TextField} from '@mui/material';
import {snackActions} from "../../utilities/Snackbar";
import {MeContext} from "../../App";
import {EventGroupTable} from "./EventGroupTable";
import {MythicDraggableDialogTitle} from "../../MythicComponents/MythicDraggableDialogTitle";
const triggerManualMutation = gql(`
mutation triggerManualMutation($eventgroup_id: Int!, $env_data: jsonb){
@@ -203,7 +204,7 @@ export function EventTriggerContextSelectDialog({onClose, triggerContext}) {
}
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">Trigger a workflow</DialogTitle>
<MythicDraggableDialogTitle >Trigger a workflow</MythicDraggableDialogTitle>
<DialogContent dividers={true} style={{maxHeight: "calc(70vh)"}}>
<DialogContentText>
Trigger a workflow with selected context and optional additional data.
@@ -220,7 +221,7 @@ export function EventTriggerContextSelectDialog({onClose, triggerContext}) {
}
</Select>
{selectedEventGroup.id !== 0 &&
<EventGroupTable me={me} selectedEventGroup={selectedEventGroup} showInstances={false} showGraph={false}/>
<EventGroupTable me={me} selectedEventGroup={selectedEventGroup} showInstances={false} showGraph={false} height={"unset"}/>
}
<Table>
<TableHead>
@@ -59,8 +59,8 @@ export function ExpandedCallbackSideDetailsTable(props){
<Table size="small" style={{tableLayout: "fixed"}}>
<TableHead>
<TableRow>
<TableCell style={{width: "12rem"}}></TableCell>
<TableCell></TableCell>
<TableCell style={{width: "12rem"}}>Callback Info</TableCell>
<TableCell>Value</TableCell>
</TableRow>
</TableHead>
<TableBody style={{whiteSpace: "pre-wrap", wordBreak: "break-all"}}>
@@ -0,0 +1,7 @@
export const Hasura = () => {
return (
<iframe height={"100%"} width={"100%"} style={{border:"none"}}
src={"/console"} title={"Hasura Console"} />
)
}
@@ -1,156 +0,0 @@
import React from 'react';
import { useQuery, gql} from '@apollo/client';
import LinearProgress from '@mui/material/LinearProgress';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableCell from '@mui/material/TableCell';
import TableContainer from '@mui/material/TableContainer';
import TableHead from '@mui/material/TableHead';
import TableRow from '@mui/material/TableRow';
import Paper from '@mui/material/Paper';
import {useTheme} from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import { MythicStyledTooltip } from '../../MythicComponents/MythicStyledTooltip';
const GetC2ProfilesAndPayloadTypes = gql`
query GetC2AndPayloadType {
c2profile(where: {deleted: {_eq: false}}) {
name
id
container_running
}
payloadtype(where: {deleted: {_eq: false}, agent_type: {_eq: "agent"}}) {
name
wrapper
id
container_running
payloadtypec2profiles {
c2profile {
name
id
}
}
}
wrappers: payloadtype(where: {deleted: {_eq: false}, agent_type: {_eq: "agent"}, wrapper: {_eq: true}}) {
name
wrapper
id
container_running
wrap_these_payload_types {
wrapped {
wrapper
id
name
}
}
}
}
`;
export function AgentC2Overview(props){
const theme = useTheme();
const [c2Profiles, setC2Profiles] = React.useState([]);
const [payloadTypeRows, setPayloadTypeRows] = React.useState([]);
const [payloadTypeRowsNoWrappers, setPayloadTypeRowsNoWrappers] = React.useState([]);
const [wrappers, setWrappers] = React.useState([]);
const { loading } = useQuery(GetC2ProfilesAndPayloadTypes, {fetchPolicy: "network-only",
onCompleted: (data) => {
const c2Headers = data.c2profile.map( (c2) => c2.name);
const payloadRows = data.payloadtype.map( (payload) => {
const payloadc2 = payload.payloadtypec2profiles.map( (c2) => {
return c2.c2profile.name;
})
return {name: payload.name, payloadtypec2profiles: payloadc2, wrapper: payload.wrapper };
});
c2Headers.sort();
payloadRows.sort( (a,b) => a.name < b.name ? -1 : 1);
const payloadTypeNoWrappers = payloadRows.filter( p => !p.wrapper);
const wrapperRows = data.wrappers.map( (payload) => {
const wrapped = payload.wrap_these_payload_types.map( (w) => {
return w.wrapped.name;
});
return {name: payload.name, wrapped}
});
wrapperRows.sort( (a,b) => a.name < b.name ? -1 : 1);
setWrappers(wrapperRows);
setC2Profiles(c2Headers);
setPayloadTypeRows(payloadRows);
setPayloadTypeRowsNoWrappers(payloadTypeNoWrappers);
},
onError: (data) => {
}
});
if (loading) {
return <LinearProgress />;
}
return (
<div style={{ marginTop: "10px", marginRight: "5px"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main, marginBottom: "5px", marginTop: "10px", marginRight: "5px"}} variant={"elevation"}>
<Typography variant="h3" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Agent / C2 Overview
</Typography>
</Paper>
<TableContainer component={Paper} className="mythicElement">
<Table size="small">
<TableHead>
<TableRow hover>
<TableCell></TableCell>
{c2Profiles.map( (c2) => (
<TableCell key={c2}>{c2}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{payloadTypeRowsNoWrappers.map( (payload) => (
<TableRow key={payload.name} hover>
<TableCell>{payload.name}</TableCell>
{c2Profiles.map( (c2) => (
<TableCell key={'payload' + c2}>
{payload.payloadtypec2profiles.includes(c2) ?
<MythicStyledTooltip title={payload.name + " supports " + c2}>
<CheckCircleIcon color="success"/>
</MythicStyledTooltip>
: ""}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
{wrappers.length > 0 &&
<TableContainer component={Paper} className="mythicElement" style={{marginTop: "10px"}}>
<Table size="small">
<TableHead>
<TableRow hover>
<TableCell></TableCell>
{payloadTypeRows.map( (pt) => (
<TableCell key={'wrapped' + pt.name}>{pt.name}</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{wrappers.map( (payload) => (
<TableRow key={'wrapper' + payload.name} hover>
<TableCell>{payload.name}</TableCell>
{payloadTypeRows.map( (wr) => (
<TableCell key={'payload' + wr.name}>
{payload.wrapped.includes(wr.name) ?
<MythicStyledTooltip title={payload.name + " wraps " + wr.name}>
<CheckCircleIcon color="success"/>
</MythicStyledTooltip>
: ""}
</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
}
</div>
);
}
@@ -57,6 +57,7 @@ import {copyStringToClipboard} from "../../utilities/Clipboard";
import MythicStyledTableCell from "../../MythicComponents/MythicTableCell";
import {faCopy} from '@fortawesome/free-solid-svg-icons';
import MythicTextField from "../../MythicComponents/MythicTextField";
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
const LeadDashboardQuery = gql`
${taskingDataFragment}
@@ -1521,13 +1522,7 @@ const Top10RecentScreenshotsDashboardElement = ({me, data, editing, removeElemen
<>
<TableDataCard title={"Recent Screenshots"}
tableHead={
<TableHead>
<TableRow>
<MythicTableCell></MythicTableCell>
<MythicTableCell></MythicTableCell>
<MythicTableCell></MythicTableCell>
</TableRow>
</TableHead>
null
}
tableBody={
<TableBody>
@@ -1594,11 +1589,7 @@ const Top10RecentTasksDashboardElement = ({me, data, editing, removeElement}) =>
<>
<TableDataCard title={"Recent Tasking"}
tableHead={
<TableHead>
<TableRow>
<MythicTableCell></MythicTableCell>
</TableRow>
</TableHead>
null
}
tableBody={
<TableBody>
@@ -1714,20 +1705,20 @@ const OperatorDashboard = ({me, setLoading, loading}) => {
}
return (
<>
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px", marginTop: "5px"}}>
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem",}}>
<ActiveCallbacksDashboardElement me={me} data={analysisData}/>
<Top10RecentPayloadsDashboardElement me={me} data={analysisData}/>
<Top10RecentWorkflowsDashboardElement me={me} data={analysisData}/>
</div>
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
<ProxyUsageDashboardElement me={me} data={analysisData}/>
<Top10RecentFileDownloadsDashboardElement me={me} data={analysisData}/>
<Top10RecentCredentialsDashboardElement me={me} data={analysisData}/>
</div>
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
<Top10RecentTasksDashboardElement me={me} data={analysisData}/>
</div>
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
<Top10RecentScreenshotsDashboardElement me={me} data={analysisData}/>
<MyOperationsDashboardElement me={me} data={analysisData} reloadDashboard={reloadDashboard}/>
</div>
@@ -1754,16 +1745,16 @@ const LeadDashboard = ({me, setLoading, loading}) => {
}, []);
return (
<>
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px", marginTop: "5px"}}>
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem",}}>
<ActiveCallbacksDashboardElement me={me} data={analysisData} />
<Top10CommandStatsDashboardElement me={me} data={analysisData} />
<Top10UserContextsDashboardElement me={me} data={analysisData} />
<Top10HostContextsDashboardElement me={me} data={analysisData} />
</div>
<div style={{display: "flex", marginLeft: "5px", marginBottom: "5px"}}>
<div style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem"}}>
<ActivityPerDayDashboardElement me={me} data={analysisData} />
</div>
<div style={{display: "flex", marginLeft: "5px"}}>
<div style={{display: "flex", marginLeft: "0.5rem"}}>
<TaskStatusDashboardElement me={me} data={analysisData} />
<OperatorActivityDashboardElement me={me} data={analysisData} />
<Top10ArtifactsDashboardElement me={me} data={analysisData} />
@@ -1932,7 +1923,7 @@ const CustomDashboard = ({me, setLoading, loading, editing}) => {
<div style={{height: "100%", overflowY: "auto"}}>
{dashboards.map((d, i) => (
<div key={"dashboardRow" + i}
style={{display: "flex", marginLeft: "5px", marginTop: "5px", borderRadius: "4px", }}>
style={{display: "flex", marginLeft: "0.5rem", marginBottom: "0.5rem", borderRadius: "5px", }}>
{editing &&
<div style={{display: "flex", flexDirection: "column", width: "40px", justifyContent: "center"}}>
<MythicStyledTooltip title={"Add New Chart To This Row"}>
@@ -1976,7 +1967,6 @@ const CustomDashboard = ({me, setLoading, loading, editing}) => {
}
export function CallbacksCard({me}) {
const theme = useTheme();
const [loading, setLoading] = React.useState(true);
const [editing, setEditing] = React.useState(false);
const initialDashboardView = GetMythicSetting({
@@ -1992,24 +1982,20 @@ export function CallbacksCard({me}) {
setLoading(true);
}
return (
<div style={{width: "100%", height: "100%", display: "flex", flexDirection: "column", position: "relative"}}>
<Paper elevation={5} variant={"elevation"} style={{
backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main,
display: "flex", alignItems: "center", justifyContent: "space-between"
}}
>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "10px"}}>
<>
<MythicPageHeader title={
<>
Welcome <b>{me.user.username}</b>
{me.user.current_operation_id === 0 ? null : (
<>
{" to"} <b>{me.user.current_operation}</b>'s Dashboard
{" to"} <b>{me.user.current_operation}</b>'s Dashboard
</>
)}
</Typography>
<span>
</>
}>
<div style={{display: "flex", alignItems: "center"}}>
<TextField
style={{color: "white", marginTop: "5px", width: "10rem"}}
style={{color: "white", width: "10rem", margin: "0.5rem"}}
InputProps={{style: {color: "white"}}}
size={"small"}
select={true}
@@ -2022,8 +2008,8 @@ export function CallbacksCard({me}) {
<MenuItem key={"searchopt" + opt} value={opt}>{opt}</MenuItem>
))
}
</TextField>
<MythicStyledTooltip title={"Analyze Operation Data Again"}>
</TextField>
<MythicStyledTooltip title={"Analyze Operation Data Again"} tooltipStyle={{display: "inline-block"}}>
<IconButton style={{color: "white"}} onClick={() => setLoading(true)}>
<ReplayIcon />
</IconButton>
@@ -2035,8 +2021,8 @@ export function CallbacksCard({me}) {
</IconButton>
</MythicStyledTooltip>
}
</span>
</Paper>
</div>
</MythicPageHeader>
<div style={{width: "100%", height: "100%", display: "flex", flexDirection: "column", position: "relative", overflowY: "auto"}}>
{loading &&
<div style={{
@@ -2070,7 +2056,7 @@ export function CallbacksCard({me}) {
me={me} loading={loading} setLoading={setLoading} editing={editing}
/>}
</div>
</div>
</>
);
}
@@ -16,6 +16,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
import IconButton from '@mui/material/IconButton';
import VisibilityOffIcon from '@mui/icons-material/VisibilityOff';
import VisibilityIcon from '@mui/icons-material/Visibility';
import Paper from "@mui/material/Paper";
const normalColors = [
'#09bdff',
@@ -45,16 +46,17 @@ export const PieChartCard = ({
const toggleLegend = () => {
setShowLegend(!showLegend);
}
const theme = useTheme();
return (
<div style={{
marginRight: "5px",
<Paper variant={"elevation"} elevation={1} style={{
marginRight: "0.5rem",
width: width,
height: "100%",
border: "1px solid gray",
borderRadius: "4px",
border: `1px solid ${theme.borderColor}`,
borderRadius: "5px",
overflow: "hidden",
}}>
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0,}}>
<h3 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0,}}>
{editing &&
<span>
<MythicStyledTooltip title={"Remove element"}>
@@ -120,7 +122,7 @@ export const PieChartCard = ({
}}>
{innerElement}
</PieChart>
</div>
</Paper>
);
}
function GaugePointer() {
@@ -151,8 +153,7 @@ export const GaugeCard = ({data, width = "100%", additionalStyles, innerElement,
right: 10,
top: 10,
bottom: 10,
}, colors = normalColors, onClick, title = "", editing, removeElement, customizeElement
}) => {
}, colors = normalColors, onClick, title = "", editing, removeElement, customizeElement }) => {
const theme = useTheme();
const getFillColor = () => {
if(data['total'] === 0){return theme.palette.text.disabled}
@@ -166,15 +167,15 @@ export const GaugeCard = ({data, width = "100%", additionalStyles, innerElement,
}
}
return (
<div style={{
marginRight: "5px",
<Paper variant={"elevation"} elevation={1} style={{
marginRight: "0.5rem",
width: width,
height: "100%",
border: "1px solid gray",
border: `1px solid ${theme.borderColor}`,
borderRadius: "5px",
overflow: "hidden",
borderRadius: "4px",
}}>
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0,}}>
<h3 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0,}}>
{editing &&
<span>
<MythicStyledTooltip title={"Remove element"}>
@@ -208,21 +209,22 @@ export const GaugeCard = ({data, width = "100%", additionalStyles, innerElement,
})}
>
</Gauge>
</div>
</Paper>
);
}
export const CallbackDataCard = ({mainTitle, secondTitle, mainElement, secondaryElement, width="100%",
editing, removeElement}) => {
const theme = useTheme();
return (
<div style={{
marginRight: "5px",
<Paper variant={"elevation"} elevation={1} style={{
marginRight: "0.5rem",
width: width,
height: "100%",
border: "1px solid gray",
border: `1px solid ${theme.borderColor}`,
borderRadius: "5px",
overflow: "hidden",
borderRadius: "4px",
}} >
<h5 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0}}>
<h2 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0}}>
{editing &&
<span >
<MythicStyledTooltip title={"Remove element"}>
@@ -233,7 +235,7 @@ export const CallbackDataCard = ({mainTitle, secondTitle, mainElement, secondary
</span>
}
{mainTitle}
</h5>
</h2>
<div style={{height: 180, cursor: "pointer"}}>
<MythicStyledTooltip title={"Go to Active Callbacks"}>
{mainElement}
@@ -243,43 +245,46 @@ export const CallbackDataCard = ({mainTitle, secondTitle, mainElement, secondary
{secondaryElement}
</MythicStyledTooltip>
</div>
</div>
</Paper>
)
}
export const TableDataCard = ({title, width = "100%", tableHead, tableBody, editing, removeElement, customizeElement}) => {
const theme = useTheme();
return (
<div style={{
marginRight: "5px",
<Paper variant={"elevation"} elevation={1} style={{
marginRight: "0.5rem",
width: width,
height: "100%",
border: "1px solid gray",
borderRadius: "4px",
border: `1px solid ${theme.borderColor}`,
borderRadius: "5px",
overflow: "hidden",
}}>
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0}}>
<h3 style={{marginTop: 0, marginLeft: "0.5rem", marginBottom: 0, paddingBottom: 0}}>
{editing &&
<span >
<MythicStyledTooltip title={"Remove element"}>
<IconButton onClick={removeElement} >
<DeleteIcon color={"error"}/>
</IconButton>
</MythicStyledTooltip>
</span>
<MythicStyledTooltip title={"Remove element"}>
<IconButton onClick={removeElement} >
<DeleteIcon color={"error"}/>
</IconButton>
</MythicStyledTooltip>
</span>
}
{title}
<span style={{float: "right", marginRight: "5px"}}>
<span style={{float: "right", marginRight: "0.5rem"}}>
{customizeElement}
</span>
</h3>
<div style={{height: 200, overflowY: "auto"}}>
<Table style={{ "maxWidth": "100%", "overflow": "auto"}} stickyHeader size="small">
<div style={{height: 200, overflowY: "auto", width: "100%"}}>
<Table style={{ maxWidth: "100%", overflow: "auto"}} stickyHeader size="small">
{tableHead}
{tableBody}
</Table>
</div>
</div>
</Paper>
)
}
export const LineTimeChartCard = ({data, additionalStyles}) => {
const theme = useTheme();
const [value, setValue] = React.useState([0, 0]);
const [range, setRange] = React.useState([0, 0]);
React.useEffect( () => {
@@ -309,12 +314,12 @@ export const LineTimeChartCard = ({data, additionalStyles}) => {
}
};
return (
<div style={{
marginBottom: "5px",
marginTop: "10px",
<Paper variant={"elevation"} elevation={1} style={{
marginRight: "0.5rem",
width: "100%",
height: "100%",
border: "1px solid gray",
border: `1px solid ${theme.borderColor}`,
borderRadius: "5px",
overflow: "hidden",
}} >
<Typography variant={"h3"} style={{margin: 0, padding: 0, position: "relative", left: "30%"}}>
@@ -369,11 +374,12 @@ export const LineTimeChartCard = ({data, additionalStyles}) => {
max={range[1]}
sx={{ mt: 2, width: "80%", left: "10%" }}
/>
</div>
</Paper>
)
}
export const LineTimeMultiChartCard = ({data, additionalStyles, colors=normalColors, view_utc_time, editing, removeElement, customizeElement}) => {
const theme = useTheme();
const [value, setValue] = React.useState([0, 0]);
const [range, setRange] = React.useState([0, 0]);
React.useEffect( () => {
@@ -423,13 +429,13 @@ export const LineTimeMultiChartCard = ({data, additionalStyles, colors=normalCol
}
}
return (
<div style={{
<Paper variant={"elevation"} elevation={1} style={{
width: "100%",
marginRight: "0.5rem",
height: "100%",
marginRight: "5px",
border: "1px solid gray",
border: `1px solid ${theme.borderColor}`,
borderRadius: "5px",
overflow: "hidden",
borderRadius: "4px",
}} >
<Typography variant={"h3"} style={{margin: 0, padding: 0, position: "relative", left: "30%"}}>
{editing &&
@@ -500,7 +506,7 @@ export const LineTimeMultiChartCard = ({data, additionalStyles, colors=normalCol
max={range[1]}
sx={{ mt: 2, width: "80%", left: "10%" }}
/>
</div>
</Paper>
)
}
@@ -509,12 +515,14 @@ export const StackedBarChartCard = ({data, labels, title, width="100%", hidden,
top: 40,
bottom: 10,
}}) => {
const theme = useTheme();
return (
<div style={{
marginRight: "5px",
<Paper variant={"elevation"} elevation={1} style={{
marginRight: "0.5rem",
width: width,
height: "100%",
border: "1px solid gray",
border: `1px solid ${theme.borderColor}`,
borderRadius: "5px",
overflow: "hidden",
}} >
<h3 style={{marginTop: 0, marginLeft: "5px", marginBottom: 0, paddingBottom: 0, position: "absolute"}}>
@@ -544,6 +552,6 @@ export const StackedBarChartCard = ({data, labels, title, width="100%", hidden,
}
}
}} />
</div>
</Paper>
);
}
@@ -1,11 +1,12 @@
import React from 'react';
import {CallbacksCard} from "./CallbacksCard";
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
export function Home({me}) {
return (
<div style={{width: "100%", height: "100%", display: "flex"}}>
<MythicPageBody>
<CallbacksCard me={me} />
</div>
</MythicPageBody>
);
}
@@ -0,0 +1,7 @@
export const Jupyter = () => {
return (
<iframe height={"100%"} width={"100%"} style={{border:"none"}}
src={"/jupyter"} title={"Jupyter Notebooks"} />
)
}
@@ -6,6 +6,7 @@ import { Backdrop } from '@mui/material';
import {CircularProgress} from '@mui/material';
import { MythicSelectFromListDialog } from '../../MythicComponents/MythicSelectFromListDialog';
import { MythicDialog } from '../../MythicComponents/MythicDialog';
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
const Get_MITREATTACK = gql`
query GetMitreAttack {
@@ -367,7 +368,7 @@ export function MitreAttack({me}){
getTasksFilteredByTag({variables: {tasks: taskIds}})
}
return (
<React.Fragment>
<MythicPageBody>
<Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute"}} invisible={false}>
<CircularProgress color="inherit" disableShrink />
</Backdrop>
@@ -388,7 +389,7 @@ export function MitreAttack({me}){
action={"select"} identifier={"cmd"} display={"cmd"}/>}
/>
}
</React.Fragment>
</MythicPageBody>
);
}
@@ -16,6 +16,7 @@ import {CircularProgress} from '@mui/material';
import { MythicDisplayTextDialog} from '../../MythicComponents/MythicDisplayTextDialog';
import { MythicDialog } from '../../MythicComponents/MythicDialog';
import { SelectPayloadTypeDialog } from './SelectPayloadTypeDialog';
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFiltered, onGetTasksFiltered, onFilterByTags, showCountGrouping}){
@@ -23,12 +24,8 @@ export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFilt
const [backdropOpen, setBackdropOpen] = React.useState(false);
return (
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main,
marginBottom: "5px", marginRight: "5px"}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
{"MITRE ATT&CK Mappings"}
</Typography>
<>
<MythicPageHeader title={"MITRE ATT&CK Mappings"}>
<PoperDropdown onGetCommands={onGetCommands}
onGetTasks={onGetTasks}
onGetCommandsFiltered={onGetCommandsFiltered}
@@ -38,7 +35,7 @@ export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFilt
showCountGrouping={showCountGrouping}
entries={entries}
/>
</Paper>
</MythicPageHeader>
<div style={{display: "flex", flexGrow: 1, overflow: "auto"}}>
{backdropOpen && <Backdrop open={backdropOpen} style={{zIndex: 2, position: "absolute"}} invisible={false}>
@@ -47,7 +44,7 @@ export function MitreGrid({entries, onGetCommands, onGetTasks, onGetCommandsFilt
}
<MitreGridDisplay entries={entries} showCountGrouping={showCountGrouping} />
</div>
</div>
</>
)
}
@@ -18,6 +18,7 @@ import DeleteIcon from '@mui/icons-material/Delete';
import IconButton from '@mui/material/IconButton';
import {MythicConfirmDialog} from '../../MythicComponents/MythicConfirmDialog';
import TuneIcon from '@mui/icons-material/Tune';
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
const newBlockListEntry = gql`
mutation newBlockListsEntries($entries: [disabledcommandsprofile_insert_input!]!) {
@@ -174,14 +175,11 @@ export function CommandBlockListTable(props){
return null;
}
return (
<React.Fragment>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Command Block Lists
</Typography>
<>
<MythicPageHeader title={"Command Block Lists"}>
<Button size="small"
onClick={()=>{setOpenNewDialog(true);}}
style={{marginRight: "10px", float: "right", color: "white"}}
style={{color: "white", whiteSpace: "nowrap"}}
startIcon={<AddCircleIcon color="success" style={{backgroundColor: "white", borderRadius: "10px"}}/>}
>New Block List</Button>
{openNew &&
@@ -191,7 +189,7 @@ export function CommandBlockListTable(props){
/>}
/>
}
</Paper>
</MythicPageHeader>
<TableContainer className="mythicElement">
<Table size="small" style={{"tableLayout": "fixed", "maxWidth": "calc(100vw)", "overflow": "scroll"}}>
<TableHead>
@@ -211,7 +209,7 @@ export function CommandBlockListTable(props){
</TableBody>
</Table>
</TableContainer>
</React.Fragment>
</>
)
}
@@ -22,6 +22,7 @@ import { MythicStyledTooltip } from '../../MythicComponents/MythicStyledTooltip'
import { IconButton } from '@mui/material';
import PersonAddIcon from '@mui/icons-material/PersonAdd';
import {meState} from "../../../cache";
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
const newOperatorMutation = gql`
mutation NewOperator($username: String!, $password: String!) {
@@ -143,105 +144,100 @@ export function OperationTable(props){
newOperation({variables: {name: operation_name}})
}
return (
<React.Fragment>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Operations
</Typography>
{showDeleted ? (
<MythicStyledTooltip title={"Hide Deleted Operations"} tooltipStyle={{float: "right"}}>
<IconButton size="small" style={{float: "right", marginRight: "10px"}}
variant="contained" onClick={() => setShowDeleted(!showDeleted)}>
<VisibilityIcon />
<>
<MythicPageHeader title={"Operations"}>
<Button size="small" onClick={() => {setOpenNewOperationDialog(true);}}
style={{ color: "white", whiteSpace: "nowrap"}}
startIcon={<AddCircleIcon color="success" style={{backgroundColor: "white", borderRadius: "10px"}}/>} >
New Operation
</Button>
<MythicStyledTooltip title={"Create new operator"} tooltipStyle={{}}>
<IconButton size="small"
onClick={()=>{setOpenNewOperatorDialog(true);}}
style={{}}
variant="contained">
<PersonAddIcon />
</IconButton>
</MythicStyledTooltip>
) : (
<MythicStyledTooltip title={"Show Deleted Operations"} tooltipStyle={{float: "right"}}>
<IconButton size="small" style={{float: "right", marginRight: "10px"}}
variant="contained" onClick={() => setShowDeleted(!showDeleted)} >
<VisibilityOffIcon />
</IconButton>
</MythicStyledTooltip>
)}
<MythicStyledTooltip title={"Create new operator"} tooltipStyle={{float: "right"}}>
<IconButton size="small"
onClick={()=>{setOpenNewOperatorDialog(true);}}
style={{marginRight: "10px", float: "right"}}
variant="contained">
<PersonAddIcon />
</IconButton>
</MythicStyledTooltip>
<Button size="small" onClick={() => {setOpenNewOperationDialog(true);}}
style={{marginRight: "20px", float: "right", color: "white"}}
startIcon={<AddCircleIcon color="success" style={{backgroundColor: "white", borderRadius: "10px"}}/>} >
New Operation
</Button>
{showDeleted ? (
<MythicStyledTooltip title={"Hide Deleted Operations"} tooltipStyle={{}}>
<IconButton size="small" style={{}}
variant="contained" onClick={() => setShowDeleted(!showDeleted)}>
<VisibilityIcon />
</IconButton>
</MythicStyledTooltip>
) : (
<MythicStyledTooltip title={"Show Deleted Operations"} tooltipStyle={{}}>
<IconButton size="small" style={{}}
variant="contained" onClick={() => setShowDeleted(!showDeleted)} >
<VisibilityOffIcon />
</IconButton>
</MythicStyledTooltip>
)}
{openNewOperator &&
<MythicDialog open={openNewOperator}
onClose={()=>{setOpenNewOperatorDialog(false);}}
innerDialog={<SettingsOperatorDialog title="New Operator" onAccept={onSubmitNewOperator} handleClose={()=>{setOpenNewOperatorDialog(false);}} {...props}/>}
/>
}
{openNewOperation &&
<MythicDialog
fullWidth={true}
open={openNewOperation}
onClose={() => {setOpenNewOperationDialog(false);}}
innerDialog={
<MythicModifyStringDialog title={"New Operation's Name"}
onClose={() => {setOpenNewOperationDialog(false);}}
value={""}
onSubmit={onSubmitNewOperation}
/>
}
/>
}
{openNewOperator &&
<MythicDialog open={openNewOperator}
onClose={()=>{setOpenNewOperatorDialog(false);}}
innerDialog={<SettingsOperatorDialog title="New Operator" onAccept={onSubmitNewOperator} handleClose={()=>{setOpenNewOperatorDialog(false);}} {...props}/>}
/>
}
{openNewOperation &&
<MythicDialog
fullWidth={true}
open={openNewOperation}
onClose={() => {setOpenNewOperationDialog(false);}}
innerDialog={
<MythicModifyStringDialog title={"New Operation's Name"}
onClose={() => {setOpenNewOperationDialog(false);}}
value={""}
onSubmit={onSubmitNewOperation}
/>
}
/>
}
</Paper>
<TableContainer className="mythicElement">
{props.operations.length === 0 &&
<div style={{display: "flex", flexDirection: "column", flexGrow: 1}}>
<div style={{
position: "absolute",
left: "35%",
top: "50%"
}}>
{"No Operations available!"}<br/>
{"Ask a Mythic admin or operation lead to add you to an operation."}
</MythicPageHeader>
<TableContainer className="mythicElement">
{props.operations.length === 0 &&
<div style={{display: "flex", flexDirection: "column", flexGrow: 1}}>
<div style={{
position: "absolute",
left: "35%",
top: "50%"
}}>
{"No Operations available!"}<br/>
{"Ask a Mythic admin or operation lead to add you to an operation."}
</div>
</div>
</div>
}
<Table size="small" style={{"tableLayout": "fixed", "overflow": "scroll"}}>
<TableHead>
<TableRow>
<TableCell style={{width: "2rem"}}></TableCell>
<TableCell style={{width: "8rem"}}>Configure</TableCell>
<TableCell style={{width: "8rem"}}>Operators</TableCell>
<TableCell>Operation Name</TableCell>
<TableCell>Operation Admin</TableCell>
<TableCell style={{width: "8rem"}}>Analysis</TableCell>
<TableCell style={{width: "11rem"}}>Operation Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.operations.map((op) => (
showDeleted || !op.deleted ? (
<OperationTableRow
me={props.me}
key={"operation" + op.id}
onUpdateOperation={onUpdateOperation}
onUpdateCurrentOperation={props.onUpdateCurrentOperation}
updateDeleted={props.updateDeleted}
{...op} operator={props.operator}
/>
) : null
))}
</TableBody>
</Table>
</TableContainer>
</React.Fragment>
}
<Table size="small" style={{"tableLayout": "fixed", "overflow": "scroll"}}>
<TableHead>
<TableRow>
<TableCell style={{width: "2rem"}}></TableCell>
<TableCell style={{width: "8rem"}}>Configure</TableCell>
<TableCell style={{width: "8rem"}}>Operators</TableCell>
<TableCell>Operation Name</TableCell>
<TableCell>Operation Admin</TableCell>
<TableCell style={{width: "8rem"}}>Analysis</TableCell>
<TableCell style={{width: "11rem"}}>Operation Status</TableCell>
</TableRow>
</TableHead>
<TableBody>
{props.operations.map((op) => (
showDeleted || !op.deleted ? (
<OperationTableRow
me={props.me}
key={"operation" + op.id}
onUpdateOperation={onUpdateOperation}
onUpdateCurrentOperation={props.onUpdateCurrentOperation}
updateDeleted={props.updateDeleted}
{...op} operator={props.operator}
/>
) : null
))}
</TableBody>
</Table>
</TableContainer>
</>
)
}
@@ -4,6 +4,7 @@ import {useQuery, gql, useLazyQuery} from '@apollo/client';
import {CommandBlockListTable} from './CommandBlockListTable';
import { snackActions } from '../../utilities/Snackbar';
import {useMythicLazyQuery} from "../../utilities/useMythicLazyQuery";
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
const GET_Operations = gql`
query GetOperations {
@@ -114,13 +115,13 @@ export function Operations(props){
getBlockLists().then(({data}) => getBlockListsSuccess(data)).catch(({data}) => getBlockListsError(data));
}, []);
return (
<div style={{ height: "100%", display: "flex", flexDirection: "column"}}>
<MythicPageBody>
<OperationTable operations={operations}
onUpdateOperation={onUpdateOperation}
onNewOperation={onNewOperation} me={props.me}
onUpdateCurrentOperation={onUpdateCurrentOperation}
updateDeleted={updateDeleted}/>
<CommandBlockListTable blockLists={blockLists} me={props.me} />
</div>
</MythicPageBody>
);
}
@@ -21,6 +21,8 @@ import VisibilityIcon from '@mui/icons-material/Visibility';
import { IconButton } from '@mui/material';
import {useTheme} from '@mui/material/styles';
import {CustomBrowserRow} from "./CustomBrowserCard";
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
const SUB_Payload_Types = gql`
subscription getPayloadTypesSubscription {
@@ -215,11 +217,8 @@ export function PayloadTypesC2Profiles({me}){
}
}
return (
<div style={{ height: "100%", display: "flex", flexDirection: "column", width: "100%"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Installed Services
</Typography>
<MythicPageBody>
<MythicPageHeader title={"Installed Services"}>
{showDeleted ? (
<MythicStyledTooltip title={"Hide Deleted Services"} tooltipStyle={{float: "right"}}>
<IconButton size="small" style={{float: "right"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)}><VisibilityIcon /></IconButton>
@@ -230,7 +229,7 @@ export function PayloadTypesC2Profiles({me}){
<IconButton size="small" style={{float: "right"}} variant="contained" onClick={() => setShowDeleted(!showDeleted)} ><VisibilityOffIcon /></IconButton>
</MythicStyledTooltip>
)}
</Paper>
</MythicPageHeader>
<AppBar position="static" color="default" className={"no-box-shadow"}>
<Tabs
value={value}
@@ -285,7 +284,7 @@ export function PayloadTypesC2Profiles({me}){
{
getTabComponent()
}
</div>
</MythicPageBody>
);
}
@@ -5,6 +5,7 @@ import DialogContent from '@mui/material/DialogContent';
import DialogTitle from '@mui/material/DialogTitle';
import {gql, useMutation} from '@apollo/client';
import { snackActions } from '../../utilities/Snackbar';
import {DragAndDropFileUpload} from "../Callbacks/TaskParametersDialogRow";
const create_payload = gql`
mutation createPayloadMutation($payload: String!) {
@@ -31,22 +32,19 @@ export function ImportPayloadConfigDialog(props) {
createPayloadMutation({variables: {payload: fileValue.contents}}).catch( (e) => {console.log(e)} );
props.onClose();
}
const onFileChange = (evt) => {
const onFileChange = (newFile) => {
const reader = new FileReader();
reader.onload = (e) => {
const contents = e.target.result;
setFileValue({name: evt.target.files[0].name, contents: contents});
setFileValue({name: newFile.name, contents: contents});
}
reader.readAsBinaryString(evt.target.files[0]);
reader.readAsBinaryString(newFile);
}
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">Import Payload Config to Generate New Payload</DialogTitle>
<DialogContent dividers={true}>
<Button variant="contained" component="label">
{ fileValue.name === "" ? "Select File" : fileValue.name }
<input onChange={onFileChange} type="file" hidden />
</Button>
<DragAndDropFileUpload value={fileValue} multiple={false} onChange={onFileChange} />
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={props.onClose} color="primary">
@@ -3,6 +3,7 @@ import { PayloadsTable } from './PayloadsTable';
import {useMutation, gql, useSubscription, useQuery, useLazyQuery} from '@apollo/client';
import { snackActions } from '../../utilities/Snackbar';
import {getSkewedNow} from "../../utilities/Time";
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
export const payloadFragment = gql`
fragment payloadData on payload {
@@ -325,7 +326,7 @@ export function Payloads(props){
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<div style={{display: "flex", height: "100%", flexDirection: "column", position: "relative"}}>
<MythicPageBody>
<PayloadsTable pageData={pageData} me={props.me}
openBackdrop={openBackdrop}
@@ -338,7 +339,7 @@ export function Payloads(props){
onCallbacksAllowedChanged={onCallbacksAllowedChanged}
onChangeShowAutogenerated={onChangeShowAutogenerated}
/>
</div>
</MythicPageBody>
);
}
@@ -21,6 +21,7 @@ import ClickAwayListener from '@mui/material/ClickAwayListener';
import {useNavigate} from 'react-router-dom';
import Pagination from '@mui/material/Pagination';
import { Backdrop, CircularProgress } from '@mui/material';
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
export function PayloadsTable({payload, onDeletePayload, onUpdateCallbackAlert, onRestorePayload, me,
@@ -74,70 +75,68 @@ export function PayloadsTable({payload, onDeletePayload, onUpdateCallbackAlert,
setDropdownOpen(false);
};
return (
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Payloads
</Typography>
<>
<MythicPageHeader title={"Payloads"}>
<ButtonGroup variant="text" ref={dropdownAnchorRef} aria-label="split button" style={{float: "right", marginRight: "10px", color: "white"}}>
<Button size="small" style={{color: "white"}} aria-controls={dropdownOpen ? 'split-button-menu' : undefined}
aria-expanded={dropdownOpen ? 'true' : undefined}
aria-haspopup="menu"
onClick={() => setDropdownOpen(!dropdownOpen)}>
Actions <ArrowDropDownIcon />
aria-expanded={dropdownOpen ? 'true' : undefined}
aria-haspopup="menu"
onClick={() => setDropdownOpen(!dropdownOpen)}>
Actions <ArrowDropDownIcon />
</Button>
</ButtonGroup>
<Popper open={dropdownOpen} anchorEl={dropdownAnchorRef.current} role={undefined} transition disablePortal style={{zIndex: 10}}>
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper className={"dropdownMenuColored"}>
<ClickAwayListener onClickAway={() => setDropdownOpen(false)}>
<MenuList id="split-button-menu">
{dropDownOptions.map((option, index) => (
<MenuItem
key={option.name}
onClick={(event) => handleMenuItemClick(event, index)}
>
{option.name}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
{({ TransitionProps, placement }) => (
<Grow
{...TransitionProps}
style={{
transformOrigin: placement === 'bottom' ? 'center top' : 'center bottom',
}}
>
<Paper className={"dropdownMenuColored"}>
<ClickAwayListener onClickAway={() => setDropdownOpen(false)}>
<MenuList id="split-button-menu">
{dropDownOptions.map((option, index) => (
<MenuItem
key={option.name}
onClick={(event) => handleMenuItemClick(event, index)}
>
{option.name}
</MenuItem>
))}
</MenuList>
</ClickAwayListener>
</Paper>
</Grow>
)}
</Popper>
{openPayloadImport &&
<MythicDialog fullWidth={true} maxWidth="sm" open={openPayloadImport}
onClose={()=>{setOpenPayloadImport(false);}}
innerDialog={<ImportPayloadConfigDialog onClose={()=>{setOpenPayloadImport(false);}} />}
onClose={()=>{setOpenPayloadImport(false);}}
innerDialog={<ImportPayloadConfigDialog onClose={()=>{setOpenPayloadImport(false);}} />}
/>
}
</Paper>
</MythicPageHeader>
<div style={{flexGrow: 1, overflowY: "auto", height: "100%", position: "relative"}}>
{openBackdrop &&
<Backdrop open={openBackdrop} style={{zIndex: 2000, position: "absolute"}}>
<CircularProgress color="inherit" disableShrink />
</Backdrop>
}
<Table stickyHeader size="small" style={{ "maxWidth": "100%", "overflow": "auto", tableLayout: "fixed"}}>
<TableHead >
<TableRow>
<TableCell style={{width: "6rem"}}></TableCell>
<TableCell style={{width: "3rem"}}></TableCell>
<TableCell>File</TableCell>
<TableCell>Progress</TableCell>
<TableCell>Description</TableCell>
<TableCell >C2 Status</TableCell>
<TableCell>Tags</TableCell>
</TableRow>
</TableHead>
<TableBody>
<Table stickyHeader size="small" style={{ "maxWidth": "100%", "overflow": "auto", tableLayout: "fixed"}}>
<TableHead >
<TableRow>
<TableCell style={{width: "6rem"}}></TableCell>
<TableCell style={{width: "3rem"}}></TableCell>
<TableCell>File</TableCell>
<TableCell>Progress</TableCell>
<TableCell>Description</TableCell>
<TableCell >C2 Status</TableCell>
<TableCell>Tags</TableCell>
</TableRow>
</TableHead>
<TableBody>
{payload.map( (op) => (
<PayloadsTableRow
me={me}
@@ -150,15 +149,16 @@ export function PayloadsTable({payload, onDeletePayload, onUpdateCallbackAlert,
{...op}
/>
))}
</TableBody>
</Table>
</TableBody>
</Table>
</div>
<div style={{background: "transparent", display: "flex", justifyContent: "center", alignItems: "center", paddingTop: "5px", paddingBottom: "10px"}}>
<Pagination count={Math.ceil(pageData.totalCount / pageData.fetchLimit)} variant="outlined" color="primary" boundaryCount={1}
siblingCount={1} onChange={onChangePage} showFirstButton={true} showLastButton={true} style={{padding: "20px"}}/>
siblingCount={1} onChange={onChangePage} showFirstButton={true} showLastButton={true} style={{padding: "20px"}}/>
<Typography style={{paddingLeft: "10px"}}>Total Results: {pageData.totalCount}</Typography>
</div>
</div>
</>
)
}
@@ -5,12 +5,7 @@ import IconButton from '@mui/material/IconButton';
import DeleteIcon from '@mui/icons-material/Delete';
import { MythicDialog } from '../../MythicComponents/MythicDialog';
import {DetailedPayloadComparisonTable, DetailedPayloadTable} from './DetailedPayloadTable';
import Grow from '@mui/material/Grow';
import Popper from '@mui/material/Popper';
import MenuItem from '@mui/material/MenuItem';
import MenuList from '@mui/material/MenuList';
import ClickAwayListener from '@mui/material/ClickAwayListener';
import Paper from '@mui/material/Paper';
import {MythicConfirmDialog} from '../../MythicComponents/MythicConfirmDialog';
import {PayloadDescriptionDialog} from './PayloadDescriptionDialog';
import {PayloadFilenameDialog} from './PayloadFilenameDialog';
@@ -14,7 +14,7 @@ import TableRow from '@mui/material/TableRow';
import Table from '@mui/material/Table';
import TableBody from '@mui/material/TableBody';
import TableHead from '@mui/material/TableHead';
import TableContainer from '@mui/material/TableContainer';
import DialogContent from '@mui/material/DialogContent';
export function PayloadsTableRowBuildProgress(props){
const [buildProgressData, setBuildProgressData] = React.useState({
@@ -109,23 +109,26 @@ export function PayloadsTableRowBuildProgress(props){
}
}
return (
<span style={props.build_phase === "success" ? {
filter: "grayscale(1)",
opacity: 0.5} : {}}>
{buildProgressData.total_steps > 0 &&
<>
<span style={props.build_phase === "success" ? {
filter: "grayscale(1)",
opacity: 0.5} : {}}>
{buildProgressData.total_steps > 0 &&
props.payload_build_steps.map( step => (
<MythicStyledTooltip title={step.step_name} key={"buildstep" + step.step_number}>
{getButton(step)}
</MythicStyledTooltip>
))
}
{openStatusDialog &&
<MythicDialog fullWidth={true} maxWidth="lg" open={openStatusDialog}
onClose={()=>{setOpenStatusDialog(false);}}
innerDialog={<PayloadBuildStepStatusDialog step={displayData} onClose={()=>{setOpenStatusDialog(false);}} />}
/>
}
</span>
</span>
{openStatusDialog &&
<MythicDialog fullWidth={true} maxWidth="lg" open={openStatusDialog}
onClose={()=>{setOpenStatusDialog(false);}}
innerDialog={<PayloadBuildStepStatusDialog step={displayData} onClose={()=>{setOpenStatusDialog(false);}} />}
/>
}
</>
);
}
@@ -167,44 +170,43 @@ export function PayloadBuildStepStatusDialog(props) {
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">Step {props.step.step_number + 1} - {props.step.step_name}</DialogTitle>
<TableContainer className="mythicElement">
<Table size="small" style={{ "maxWidth": "100%", "overflow": "scroll"}}>
<TableHead>
<TableRow>
<TableCell style={{width: "30%"}}>Parameter</TableCell>
<TableCell >Value</TableCell>
</TableRow>
</TableHead>
<TableBody style={{whiteSpace: "pre"}}>
<TableRow hover>
<TableCell>Step Start Time</TableCell>
<TableCell>{props.step.start_time}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Step End Time</TableCell>
<TableCell>{props.step.end_time}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Duration</TableCell>
<TableCell>{duration}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Status</TableCell>
<TableCell> {getStatusMessage()}
</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Step Output</TableCell>
<TableCell>{props.step.step_stdout}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Step Error</TableCell>
<TableCell>{props.step.step_stderr}</TableCell>
</TableRow>
</TableBody>
</Table>
</TableContainer>
<DialogContent>
<Table size="small" style={{ maxWidth: "100%", overflow: "scroll"}}>
<TableHead>
<TableRow>
<TableCell style={{width: "30%"}}>Parameter</TableCell>
<TableCell >Value</TableCell>
</TableRow>
</TableHead>
<TableBody style={{whiteSpace: "pre"}}>
<TableRow hover>
<TableCell>Step Start Time</TableCell>
<TableCell>{props.step.start_time}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Step End Time</TableCell>
<TableCell>{props.step.end_time}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Duration</TableCell>
<TableCell>{duration}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Status</TableCell>
<TableCell> {getStatusMessage()}
</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Step Output</TableCell>
<TableCell>{props.step.step_stdout}</TableCell>
</TableRow>
<TableRow hover>
<TableCell>Step Error</TableCell>
<TableCell>{props.step.step_stderr}</TableCell>
</TableRow>
</TableBody>
</Table>
</DialogContent>
<DialogActions>
<Button variant="contained" onClick={props.onClose} color="primary">
Close
@@ -308,16 +310,11 @@ export function PayloadsTableRowBuildProcessPerStep(props){
}
return (
<React.Fragment>
{buildProgressData.total_steps > 0 ? (
<React.Fragment>
{props.payload_build_steps.map( step => (
step.step_number === props.step_number ? (
<React.Fragment key={"buildstep" + step.step_number}>{getButton(step)}</React.Fragment>
) : null
))}
</React.Fragment>
) : null}
{buildProgressData.total_steps > 0 &&
props.payload_build_steps.map( step => (
step.step_number === props.step_number &&
<React.Fragment key={"buildstep" + step.step_number}>{getButton(step)}</React.Fragment>
))}
{openStatusDialog &&
<MythicDialog fullWidth={true} maxWidth="lg" open={openStatusDialog}
onClose={()=>{setOpenStatusDialog(false);}}
@@ -3,18 +3,16 @@ import Paper from '@mui/material/Paper';
import {useTheme} from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import {ReportingTable} from './ReportingTable';
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
export function Reporting() {
const theme = useTheme();
return (
<div style={{height: "100%", display: "flex", flexDirection: "column"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Mythic Report Generation
</Typography>
</Paper>
<MythicPageBody>
<MythicPageHeader title={"Mythic Report Generation"}>
</MythicPageHeader>
<ReportingTable />
</div>
</MythicPageBody>
);
}
@@ -7,14 +7,21 @@ import 'ace-builds/src-noconflict/mode-json';
import 'ace-builds/src-noconflict/theme-monokai';
import 'ace-builds/src-noconflict/theme-xcode';
import {ResponseDisplayMedia} from "../Callbacks/ResponseDisplayMedia";
import {MythicDraggableDialogTitle} from "../../MythicComponents/MythicDraggableDialogTitle";
export function PreviewFileMediaDialog({agent_file_id, filename, onClose}) {
return (
const onClick = (e) => {
if(e){
e.preventDefault();
e.stopPropagation();
}
}
return (
<React.Fragment>
<DialogTitle id="form-dialog-title">
<MythicDraggableDialogTitle>
Previewing <b>{filename}</b>
</DialogTitle>
<DialogContent style={{height: "calc(95vh)", margin: 0, padding: 0}}>
</MythicDraggableDialogTitle>
<DialogContent onClick={onClick} style={{height: "calc(80vh)", margin: 0, padding: 0}}>
<ResponseDisplayMedia media={{agent_file_id, filename}} expand={true} />
</DialogContent>
<DialogActions>
@@ -3,6 +3,7 @@ import { SettingsOperatorTable } from './SettingsOperatorTable';
import {useMutation, useQuery, gql} from '@apollo/client';
import { meState } from '../../../cache';
import { snackActions } from '../../utilities/Snackbar';
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
const GET_Operator = gql`
query GetOperators {
@@ -275,7 +276,7 @@ export function Settings({me}){
}
return (
<div style={{display: "flex", height: "100%", flexDirection: "column"}}>
<MythicPageBody>
<SettingsOperatorTable
me={me}
onViewUTCChanged={onViewUTCChanged}
@@ -287,6 +288,6 @@ export function Settings({me}){
onUsernameChanged={onUsernameChanged}
onPasswordChanged={onPasswordChanged}
operators={operators} />
</div>
</MythicPageBody>
);
}
@@ -26,6 +26,8 @@ import {SettingsOperatorAPITokenSearchDialog} from "./SettingsOperatorAPITokenSe
import ForwardToInboxTwoToneIcon from '@mui/icons-material/ForwardToInboxTwoTone';
import {InviteLinksDialog} from "./InviteLinksDialog";
import {useLazyQuery} from '@apollo/client';
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
@@ -72,23 +74,43 @@ export function SettingsOperatorTable(props){
getGlobalSettings().then(({data}) => setInviteLinksEnabled(data.getGlobalSettings.settings["server_config"]["allow_invite_links"]));
}, []);
return (
<div style={{display: "flex", flexDirection: "column", height: "100%"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Settings
</Typography>
<MythicStyledTooltip title={"Adjust Global Settings"} tooltipStyle={{float: "right", marginRight: "10px", marginLeft: "5px"}}>
<>
<MythicPageHeader title={"Settings"}>
<MythicStyledTooltip title={"Manage Invite Links"} tooltipStyle={{}}>
<IconButton size={"small"} disabled={!inviteLinksEnabled || !userIsAdmin}
onClick={()=>{setOpenInviteLinksDialog(true);}} variant={"contained"} >
<ForwardToInboxTwoToneIcon />
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={"Create new user account"} tooltipStyle={{}}>
<IconButton size="small" onClick={()=>{setOpenNewDialog(true);}} variant="contained">
<PersonAddIcon/>
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={"Create new Bot account"} tooltipStyle={{}}>
<IconButton size={"small"}
onClick={()=>{setOpenNewBotDialog(true);}} variant="contained">
<SmartToyTwoToneIcon />
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={"Search for API Tokens"} tooltipStyle={{}} >
<IconButton size={"small"} variant="contained"
onClick={() => setOpenAPITokenSearch(true)}>
<SearchIcon />
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={"Adjust Global Settings"} tooltipStyle={{}}>
<IconButton size="small" variant="contained"
onClick={() => setOpenGlobalSettingsDialog(!openGlobalSettingsDialog)} >
<TuneIcon />
</IconButton>
</MythicStyledTooltip>
{showDeleted ? (
<MythicStyledTooltip title={"Hide Deleted Operators"} tooltipStyle={{float: "right"}}>
<MythicStyledTooltip title={"Hide Deleted Operators"} tooltipStyle={{}}>
<IconButton size="small" variant="contained" onClick={() => setShowDeleted(!showDeleted)}><VisibilityIcon /></IconButton>
</MythicStyledTooltip>
) : (
<MythicStyledTooltip title={"Show Deleted Operators"} tooltipStyle={{float: "right"}}>
<MythicStyledTooltip title={"Show Deleted Operators"} tooltipStyle={{}}>
<IconButton size="small" variant="contained" onClick={() => setShowDeleted(!showDeleted)} ><VisibilityOffIcon /></IconButton>
</MythicStyledTooltip>
)}
@@ -99,12 +121,6 @@ export function SettingsOperatorTable(props){
onClose={closeGlobalSettingsDialog} />}
/>
}
<MythicStyledTooltip title={"Search for API Tokens"} tooltipStyle={{float: "right", marginRight: "5px",}} >
<IconButton size={"small"} variant="contained"
onClick={() => setOpenAPITokenSearch(true)}>
<SearchIcon />
</IconButton>
</MythicStyledTooltip>
{openAPITokenSearch &&
<MythicDialog open={openAPITokenSearch} maxWidth={"xl"} fullWidth={true}
onClose={()=>{setOpenAPITokenSearch(false);}}
@@ -112,23 +128,6 @@ export function SettingsOperatorTable(props){
onClose={()=>{setOpenAPITokenSearch(false);}} />}
/>
}
<MythicStyledTooltip title={"Create new Bot account"} tooltipStyle={{float: "right", marginLeft: "5px"}}>
<IconButton size={"small"}
onClick={()=>{setOpenNewBotDialog(true);}} variant="contained">
<SmartToyTwoToneIcon />
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={"Create new user account"} tooltipStyle={{float: "right", marginLeft: "5px"}}>
<IconButton size="small" onClick={()=>{setOpenNewDialog(true);}} variant="contained">
<PersonAddIcon/>
</IconButton>
</MythicStyledTooltip>
<MythicStyledTooltip title={"Manage Invite Links"} tooltipStyle={{float: "right", marginLeft: "5px"}}>
<IconButton size={"small"} disabled={!inviteLinksEnabled || !userIsAdmin}
onClick={()=>{setOpenInviteLinksDialog(true);}} variant={"contained"} >
<ForwardToInboxTwoToneIcon />
</IconButton>
</MythicStyledTooltip>
{openInviteLinksDialog &&
<MythicDialog open={openInviteLinksDialog} maxWidth={"xl"} fullWidth={true}
onClose={()=>{setOpenInviteLinksDialog(false);}}
@@ -151,7 +150,7 @@ export function SettingsOperatorTable(props){
innerDialog={<SettingsBotDialog title="New Bot Account" onAccept={onSubmitNewBot} handleClose={()=>{setOpenNewBotDialog(false);}} {...props}/>}
/>
}
</Paper>
</MythicPageHeader>
<div style={{display: "flex", flexGrow: 1, overflowY: "auto", alignItems: "flex-start"}}>
<Table stickyHeader size="small" style={{"tableLayout": "fixed",}}>
<TableHead >
@@ -189,7 +188,7 @@ export function SettingsOperatorTable(props){
</TableBody>
</Table>
</div>
</div>
</>
)
}
@@ -242,6 +242,10 @@ export function SettingsOperatorUIConfigDialog(props) {
dark: isValidColor(initialPalette?.outputTextColor?.dark) ? initialPalette?.outputTextColor?.dark : operatorSettingDefaults.palette.outputTextColor.dark,
light: isValidColor(initialPalette?.outputTextColor?.light) ? initialPalette?.outputTextColor?.light : operatorSettingDefaults.palette.outputTextColor.light,
},
borderColor: {
dark: isValidColor(initialPalette?.borderColor?.dark) ? initialPalette?.borderColor?.dark : operatorSettingDefaults.palette.borderColor.dark,
light: isValidColor(initialPalette?.borderColor?.light) ? initialPalette?.borderColor?.light : operatorSettingDefaults.palette.borderColor.light,
},
});
const paletteOptionsSolidColor = [
{name: "primary", display: "Primary"},
@@ -264,7 +268,8 @@ export function SettingsOperatorUIConfigDialog(props) {
{name: "taskContextColor", display: "Tasking Context Generic Background Color"},
{name: "taskContextImpersonationColor", display: "Tasking Context User Background Color"},
{name: "taskContextExtraColor", display: "Tasking Context Extra Info Background Color"},
{name: "emptyFolderColor", display: "Color of the empty folder icon and text in file-based browsers"}
{name: "emptyFolderColor", display: "Color of the empty folder icon and text in file-based browsers"},
{name: "borderColor", display: "Color of borders around elements"}
]
const [resumeNotifications, setResumeNotifications] = React.useState(false);
const [_, updateSettings, clearSettings] = useSetMythicSetting();
@@ -66,11 +66,10 @@ query taskMetadataQuery($task_range: [Int!]) {
display_id
}
}`;
const lazyQueryOptions = {fetchPolicy: "no-cache"};
export function TaskMetadataTable(props){
const [tasks, setTasks] = React.useState([]);
const getMetadata = useMythicLazyQuery(MetadataQuery, {fetchPolicy: "no-cache"
});
const getMetadata = useMythicLazyQuery(MetadataQuery, lazyQueryOptions);
useEffect( () => {
getMetadata({variables: {task_range: props.taskIDs } }).then(({data}) => {
setTasks(data.task);
@@ -15,6 +15,7 @@ import {useTheme} from '@mui/material/styles';
import {taskingDataFragment} from '../Callbacks/CallbackMutations'
import {meState} from "../../../cache";
import { useReactiveVar } from '@apollo/client';
import {TaskDisplayInteractiveSearch} from "../Search/SearchTabInteractiveTasks";
const tasksQuery = gql`
${taskingDataFragment}
@@ -243,7 +244,7 @@ export function SingleTaskView(props){
}
}, [getTasks, taskId]);
return (
<div style={{height: "100%", display: "flex", flexDirection: "column", width:"100%",}}>
<div style={{height: "100%", display: "flex", flexDirection: "column", width:"100%", overflowY: 'auto',}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main, marginBottom: "5px"}} variant={"elevation"}>
<Typography variant="h4" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Task View
@@ -252,13 +253,17 @@ export function SingleTaskView(props){
onClick={getShareableLink}>Get Shareable link</Button>
<Button variant="contained" size="small" style={{display: "inline-block", float: "right", marginTop:"5px", marginRight:"10px", backgroundColor: theme.palette.warning.main}}
onClick={removeTasksButton}>Remove Tasks From View</Button>
</Paper>
{tasks.map( (task) => (
task.type === "task" ? (
<div style={{ display: "flex", flexDirection: "column"}}>
{tasks.map( (task) => (
task.type === "task" ? (
<div key={"taskdisplay:" + task.display_id} style={{marginRight: "5px"}}>
<div style={{width: removing ? "95%" : "100%", display: "inline-block"}}>
<TaskDisplay me={me} task={task} command_id={task.command === null ? 0 : task.command.id} />
{task.is_interactive_task ? (
<TaskDisplayInteractiveSearch key={"taskinteractdisplay" + task.id} me={me} task={task} responsesSurrounding={5} />
) : (
<TaskDisplay key={"taskinteractdisplay" + task.id} me={me} task={task} command_id={task.command == null ? 0 : task.command.id} />
)}
</div>
{removing ? (
<Switch
@@ -266,28 +271,31 @@ export function SingleTaskView(props){
onChange={toggleTaskToRemove}
name={"task" + task.display_id}
inputProps={{ 'aria-label': 'checkbox', 'color': theme.palette.error.main }}
/>
/>
) : null}
</div>
) : (
<Paper key={"taskdisplayforcallback:" + task.id} elevation={5} style={{ marginBottom: "5px", marginTop: "10px"}} variant={"elevation"}>
<Typography variant="h4" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
{task.domain === "" ? null : (task.domain + "\\")}{task.user}{task.integrity_level > 2 ? ("*") : null}@{task.host} (
<Link style={{wordBreak: "break-all"}} color={"textPrimary"} underline="always" target="_blank" href={"/new/callbacks/" + task.display_id}>{task.display_id}</Link>
)
</Typography>
<Button variant="contained" size="small" style={{display: "inline-block", float: "right", marginTop:"5px", marginRight:"10px", backgroundColor: theme.palette.info.main}}
onClick={() => {setTaskSearchInfo(task.display_id)}}>Include More Tasks</Button>
</Paper>
))
) : (
<Paper key={"taskdisplayforcallback:" + task.id} elevation={5} style={{ marginBottom: "5px", marginTop: "10px"}} variant={"elevation"}>
<Typography variant="h4" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
{task.domain === "" ? null : (task.domain + "\\")}{task.user}{task.integrity_level > 2 ? ("*") : null}@{task.host} (
<Link style={{wordBreak: "break-all"}} color={"textPrimary"} underline="always" target="_blank" href={"/new/callbacks/" + task.display_id}>{task.display_id}</Link>
)
</Typography>
<Button variant="contained" size="small" style={{display: "inline-block", float: "right", marginTop:"5px", marginRight:"10px", backgroundColor: theme.palette.info.main}}
onClick={() => {setTaskSearchInfo(task.display_id)}}>Include More Tasks</Button>
</Paper>
))
)
}
</div>
{openIncludeMoreTasksDialog &&
<MythicDialog fullWidth={true} maxWidth="md" open={openIncludeMoreTasksDialog}
onClose={()=>{setOpenIncludeMoreTasksDialog(false);}}
innerDialog={<IncludeMoreTasksDialog submitFetchTasks={submitIncludeMoreTasks} taskOptions={taskOptions} onClose={()=>{setOpenIncludeMoreTasksDialog(false);}} />}
/>
}
<MythicDialog fullWidth={true} maxWidth="md" open={openIncludeMoreTasksDialog}
onClose={()=>{setOpenIncludeMoreTasksDialog(false);}}
innerDialog={<IncludeMoreTasksDialog submitFetchTasks={submitIncludeMoreTasks} taskOptions={taskOptions} onClose={()=>{setOpenIncludeMoreTasksDialog(false);}} />}
/>
<TaskMetadataTable taskIDs={taskIDs} />
</div>
);
@@ -2,6 +2,7 @@ import React from 'react';
import { TagtypesTable } from './TagtypesTable';
import {useMutation, gql, useQuery} from '@apollo/client';
import { snackActions } from '../../utilities/Snackbar';
import {MythicPageBody} from "../../MythicComponents/MythicPageBody";
const tagtypeFragment = gql`
fragment tagtypeData on tagtype {
id
@@ -89,8 +90,8 @@ export function Tags(props){
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [])
return (
<div style={{display: "flex", height: "100%", flexDirection: "column"}}>
<MythicPageBody>
<TagtypesTable onDeleteTagtype={onDeleteTagtype} tagtypes={tagtypes} onNewTag={onNewTag} onUpdateTagtype={onUpdateTagtype} me={props.me}/>
</div>
</MythicPageBody>
);
}
@@ -19,6 +19,7 @@ import FileDownloadIcon from '@mui/icons-material/FileDownload';
import AddCircleIcon from '@mui/icons-material/AddCircle';
import {downloadFileFromMemory} from '../../utilities/Clipboard';
import {useMythicLazyQuery} from "../../utilities/useMythicLazyQuery";
import {MythicPageHeader} from "../../MythicComponents/MythicPageHeader";
const importTagtypesMutation = gql`
mutation importMultipleTagtypes($tagtypes: String!) {
@@ -83,11 +84,8 @@ export function TagtypesTable({tagtypes, onDeleteTagtype, onNewTag, onUpdateTagt
.then(({data}) => exportTagTypesSuccess(data)).catch(({data}) => exportTagTypesError(data));
}
return (
<div style={{display: "flex", flexDirection: "column", width: "100%", height: "100%"}}>
<Paper elevation={5} style={{backgroundColor: theme.pageHeader.main, color: theme.pageHeaderText.main}} variant={"elevation"}>
<Typography variant="h5" style={{textAlign: "left", display: "inline-block", marginLeft: "20px"}}>
Types of Tags
</Typography>
<>
<MythicPageHeader title={"Types of Tags"}>
<Button component="label" size={"small"}
style={{float:"right", marginRight: "5px", color: "white"}} >
<FileUploadIcon /> {"Import"}
@@ -105,7 +103,7 @@ export function TagtypesTable({tagtypes, onDeleteTagtype, onNewTag, onUpdateTagt
innerDialog={<NewTagtypesDialog onClose={()=>{setOpenNewDialog(false);}} onSubmit={onNewTag} />}
/>
}
</Paper>
</MythicPageHeader>
<div style={{display: "flex", flexGrow: 1, overflow: "auto"}}>
<TableContainer className="mythicElement">
<Table stickyHeader={true} size="small" style={{ "maxWidth": "100%", "overflow": "scroll"}}>
@@ -132,7 +130,7 @@ export function TagtypesTable({tagtypes, onDeleteTagtype, onNewTag, onUpdateTagt
</Table>
</TableContainer>
</div>
</div>
</>
)
}
@@ -15,7 +15,7 @@ export const useDarkMode = () => {
const localTheme = window.localStorage.getItem('theme');
if(localTheme){setTheme(localTheme)}
else{
const checkIsDarkSchemePreferred = () => window?.matchMedia?.('(prefers-color-scheme:dark)')?.matches || false;
const checkIsDarkSchemePreferred = () => window?.matchMedia?.('(prefers-color-scheme:dark)')?.matches || true;
if(checkIsDarkSchemePreferred()){
setMode('dark');
} else {
+1 -1
View File
@@ -15,7 +15,7 @@ import {jwtDecode} from 'jwt-decode';
import {meState} from './cache';
import {getSkewedNow} from "./components/utilities/Time";
export const mythicUIVersion = "0.3.101";
export const mythicUIVersion = "0.3.114";
let fetchingNewToken = false;
+25 -25
View File
@@ -34,8 +34,8 @@ html, body, #root {
.MuiTableCell-head {
background-color: ${(props) => props.theme.tableHeader} !important;
font-weight: bold;
border-top: 2px solid grey;
border-bottom: 2px solid grey;
border-top: 2px solid ${(props) => props.theme.borderColor};
border-bottom: 2px solid ${(props) => props.theme.borderColor};
}
tr:nth-child(even) {
background-color: ${(props) => props.theme.palette.mode === 'dark' ? props.theme.tableHover + "80" : props.theme.tableHover + "80"};
@@ -146,13 +146,13 @@ tspan {
opacity: 1;
}
.groupNode {
border: 1px solid grey;
border: 1px solid ${(props) => props.theme.borderColor};
padding: 5px;
border-radius: 5px;
background: ${(props) => props.theme.palette.graphGroupRGBA} !important;
}
.groupEventNode {
border: 1px solid grey;
border: 1px solid ${(props) => props.theme.borderColor};
padding: 2px;
border-radius: 5px;
background: ${(props) => props.theme.palette.graphGroupRGBA} !important;
@@ -167,7 +167,7 @@ tspan {
}
.context-menu {
background-color: ${(props) => props.theme.palette.background.paper};
border: 1px solid grey;
border: 1px solid ${(props) => props.theme.borderColor};
border-radius: 5px;
box-shadow: 10px 19px 20px rgba(0, 0, 0, 10%);
position: absolute;
@@ -212,11 +212,11 @@ tspan {
box-sizing: border-box;
justify-content: space-between;
user-select: none;
border-top: 2px solid grey;
border-bottom: 2px solid grey;
border-top: 2px solid ${(props) => props.theme.borderColor};
border-bottom: 2px solid ${(props) => props.theme.borderColor};
background-color: ${(props) => props.theme.tableHeader} !important;
&:first-child-of-type {
border-left: 2px solid grey;
border-left: 2px solid ${(props) => props.theme.borderColor};
}
&:hover {
background-color: ${(props) => props.theme.tableHover};
@@ -232,7 +232,7 @@ tspan {
padding: 0 0.5em;
box-sizing: border-box;
font-family: monospace;
border-bottom: 1px solid grey;
border-bottom: 1px solid ${(props) => props.theme.borderColor};
cursor: default !important;
}
.MythicResizableGrid-cellInner {
@@ -276,7 +276,7 @@ tspan {
width: 100%;
}
.MuiPaper-root {
border: 1px solid grey;
border: 1px solid ${(props) => props.theme.borderColor};
border-radius: 5px;
background-color: ${(props) => props.theme.palette.background.paper};
background-image: unset;
@@ -290,13 +290,13 @@ tspan {
.MuiList-root {
background-color: ${(props) => props.theme.palette.background.paper};
border: 1px solid grey;
border: 1px solid ${(props) => props.theme.borderColor};
border-radius: 3px;
line-height: 30px;
}
.dropdownMenuColored {
background-color: ${(props) => props.theme.palette.background.paper} !important;
border: 1px solid grey;
border: 1px solid ${(props) => props.theme.borderColor};
border-radius: 5px;
//color: white;
}
@@ -331,25 +331,25 @@ tspan {
border-bottom-right-radius: 4px;
}
.MuiInputLabel-root {
color: grey !important;
color: ${(props) => props.theme.borderColor} !important;
}
.MuiOutlinedInput-notchedOutline {
border-color: grey !important;
border-color: ${(props) => props.theme.borderColor} !important;
}
.MuiInput-underline {
border-color: grey !important;
border-color: ${(props) => props.theme.borderColor} !important;
}
.MuiSelect-outlined {
border-color: grey !important;
border-color: ${(props) => props.theme.borderColor} !important;
}
.Mui-focused {
border-color: grey !important;
border-color: ${(props) => props.theme.borderColor} !important;
}
.MuiInputBase-input {
border-color: grey !important;
border-color: ${(props) => props.theme.borderColor} !important;
}
.MuiInput-root::after {
border-color: grey !important;
border-color: ${(props) => props.theme.borderColor} !important;
}
.MuiTableCell-root {
padding: 0 16px 0 16px;
@@ -386,22 +386,22 @@ tspan {
border-top-left-radius: 10px;
border-top-right-radius: 10px;
padding: 0 10px 0 10px;
border-top: 1px solid grey;
border-left: 2px solid grey;
border-right: 1px solid grey;
border-bottom: 1px solid grey;
border-top: 1px solid ${(props) => props.theme.borderColor};
border-left: 2px solid ${(props) => props.theme.borderColor};
border-right: 1px solid ${(props) => props.theme.borderColor};
border-bottom: 1px solid ${(props) => props.theme.borderColor};
position: relative;
top: 2px;
word-break: break-all;
}
.empty-table-header {
border: 1px solid grey !important;
border: 1px solid ${(props) => props.theme.borderColor} !important;
height: 0px !important;
padding: 0px !important;
margin: 0px !important;
}
.code-box {
border: 1px solid grey !important;
border: 1px solid ${(props) => props.theme.borderColor} !important;
padding: 0px 5px 0px 5px;
overflow: auto;
white-space: pre;
+6
View File
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## [0.3.26] - 2026-03-16
### Changed
- Updated the docker SDK version
## [0.3.25] - 2026-01-14
### Changed
+1 -1
View File
@@ -1,5 +1,5 @@
#FROM itsafeaturemythic/mythic_go_base:latest
FROM golang:1.21-alpine
FROM golang:1.25-alpine
WORKDIR /usr/src/app
ARG GOPROXY=proxy.golang.org
+1 -1
View File
@@ -1,6 +1,6 @@
BINARY_NAME=mythic-cli
LOCAL_PATH=$(shell pwd)
BUILDER_IMAGE=ghcr.io/its-a-feature/mythic_cli:v3.4.0.44
BUILDER_IMAGE=ghcr.io/its-a-feature/mythic_cli:v3.4.0.61
.PHONY: default
default: build_linux ;
+1 -1
View File
@@ -2,5 +2,5 @@ package config
// MythicDockerLatest is the most recent tagged version pushed to GitHub packages
const MythicDockerLatest = "v3.4.0.44"
const MythicDockerLatest = "v3.4.0.61"
+7 -1
View File
@@ -110,6 +110,9 @@ func setMythicConfigDefaultValues() {
mythicEnv.SetDefault("global_restart_policy", "always")
mythicEnvInfo["global_restart_policy"] = `This sets the restart policy for the containers within Mythic. Valid options should only be 'always', 'unless-stopped', and 'on-failure'. The default of 'always' will ensure that Mythic comes back up even when the server reboots. The 'unless-stopped' value means that Mythic should come back online after reboot unless you specifically ran './mythic-cli stop' first.`
mythicEnv.SetDefault("mythic_docker_image_mirror", "")
mythicEnvInfo["mythic_docker_image_mirror"] = `This sets an internal Docker registry to pull images from instead of their original upstream registries. When empty (the default), every image is pulled from its original location (for example, "ghcr.io/its-a-feature/mythic_server:<global_docker_latest>" for core services, or whatever URL is declared in each installed agent's/C2 profile's config.json "remote_images" entry). When set to a non-empty value, the leading registry host of every image is replaced with this value while the rest of the path is preserved. For example, with mythic_docker_image_mirror="myregistry.local/mirror", the core image "ghcr.io/its-a-feature/mythic_server:0.0.5" becomes "myregistry.local/mirror/its-a-feature/mythic_server:0.0.5" and an agent image "ghcr.io/mythicagents/apollo:latest" becomes "myregistry.local/mirror/mythicagents/apollo:latest". The same rewrite is applied every time the docker-compose file is generated, so this can be changed and applied with start/build without re-installing services. This setting has no effect on services where *_use_build_context is true. Images without a registry host (for example, "nginx:latest") are treated as having no host and the full path is preserved under the mirror.`
// nginx configuration ---------------------------------------------
mythicEnv.SetDefault("nginx_port", 7443)
mythicEnvInfo["nginx_port"] = `This sets the port used for the Nginx reverse proxy - this port is used by the React UI and Mythic's Scripting`
@@ -268,6 +271,9 @@ If it's anything else, mythic-cli will not spin up this container as it assumes
mythicEnv.SetDefault("postgres_password", utils.GenerateRandomPassword(30))
mythicEnvInfo["postgres_password"] = `This is the randomly generated password that mythic_server and mythic_graphql use to connect to the mythic_postgres container`
mythicEnv.SetDefault("postgres_sslmode", "disable")
mythicEnvInfo["postgres_sslmode"] = `This specifies the SSL mode used when connecting to the PostgreSQL database (e.g. disable, require, verify-ca, verify-full)`
mythicEnv.SetDefault("postgres_cpus", defaultNumberOfCPUs)
mythicEnvInfo["postgres_cpus"] = `Set this to limit the maximum number of CPUs this service is able to consume`
@@ -678,7 +684,7 @@ func GetConfigHelp(entries []string) map[string]string {
} else if strings.HasSuffix(setting, "use_build_context") {
output[setting] = `This setting determines if you use the pre-built image hosted on GitHub/DockerHub or if you use your local InstalledService/[agent]/Dockerfile to build a new local image for your container. If you're wanting to make changes to an agent or c2 profile (adding commands, updating code, etc), then you need to set this to 'true' and update your Dockerfile to copy in your modified code. If your container code is Golang, then you'll also need to make sure your modified Dockerfile rebuilds that Go code based on your changes (probably with a 'go build' or 'make build' command depending on the agent). If your container code is Python, then simply copying in the changes should be sufficient. Also make sure you change [agent]_use_volume to 'false' so that your changes don't get overwritten by an old volume. Alternatively, you could remove the old volume with './mythic-cli volume rm [agent]_volume' and then build your new container with './mythic-cli build [agent]'.`
} else if strings.HasSuffix(setting, "remote_image") {
output[setting] = `This setting configures the remote image to use if you have *_use_build_context set to false. This value should get automatically updated by the agent/c2 profile's repo as new releases are created. This value will also get updated each time you install an agent. So if you want to pull an agent's latest image, just re-install the agent (or manually update this value and restart the local container).`
output[setting] = `This setting configures the remote image to use if you have *_use_build_context set to false. This value should get automatically updated by the agent/c2 profile's repo as new releases are created. This value will also get updated each time you install an agent. So if you want to pull an agent's latest image, just re-install the agent (or manually update this value and restart the local container). When mythic_docker_image_mirror is set, this value is kept as the upstream image URL but the image actually pulled is rewritten to come from the mirror; clear the mirror to pull from the URL shown here as-is.`
} else {
}
+1 -1
View File
@@ -4,5 +4,5 @@ package config
var (
// Version Mythic CLI version
Version = "v0.3.25"
Version = "v0.3.26"
)
@@ -39,6 +39,9 @@ func InstallFolder(installPath string, overWrite bool, keepVolume bool, installU
}
}
latestVersion := installConfig.GetStringMapString("remote_images")
if mirror := config.GetMythicEnv().GetString("mythic_docker_image_mirror"); mirror != "" {
log.Printf("[*] mythic_docker_image_mirror is set to %q; this service's image(s) will be pulled from there instead of their upstream registry\n", mirror)
}
for key, val := range latestVersion {
config.SetNewConfigStrings(fmt.Sprintf("%s_remote_image", key), val)
if !config.GetMythicEnv().InConfig(fmt.Sprintf("%s_use_volume", key)) {
@@ -87,8 +87,14 @@ func ServiceStart(containers []string, keepVolume bool) error {
log.Printf("[-] Failed to remove images\n%v\n", err)
return err
}
updateNginxBlockLists()
generateCerts()
// nginx-specific housekeeping only applies when mythic_nginx runs locally
if isServiceInternal("mythic_nginx") {
updateNginxBlockLists()
if err := generateCerts(); err != nil {
log.Printf("[-] Failed to generate certs: %v\n", err)
return err
}
}
TestMythicRabbitmqConnection()
TestMythicConnection()
Status(false)
+164 -162
View File
@@ -44,7 +44,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
pStruct["cpus"] = mythicEnv.GetInt("POSTGRES_CPUS")
@@ -67,16 +67,7 @@ func AddMythicService(service string, removeVolume bool) {
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
if mythicEnv.GetBool("postgres_bind_localhost_only") {
pStruct["command"] = "postgres -c \"max_connections=100\" -p ${POSTGRES_PORT} -c config_file=/etc/postgresql.conf -c \"listen_addresses=localhost\""
} else {
@@ -126,7 +117,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
if mythicEnv.GetString("mythic_docker_networking") == "bridge" {
if mythicEnv.GetBool("documentation_bind_localhost_only") {
@@ -146,16 +137,7 @@ func AddMythicService(service string, removeVolume bool) {
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
if mythicEnv.GetBool("documentation_bind_localhost_only") {
pStruct["environment"] = []string{
"DOCUMENTATION_PORT=${DOCUMENTATION_PORT}",
@@ -185,13 +167,21 @@ func AddMythicService(service string, removeVolume bool) {
}
}
case "mythic_graphql":
pStruct["depends_on"] = map[string]map[string]string{
"mythic_postgres": {
dependsOn := map[string]map[string]string{}
if isServiceInternal("mythic_server") {
dependsOn["mythic_server"] = map[string]string{
"condition": "service_healthy",
},
"mythic_server": {
}
}
if isServiceInternal("mythic_postgres") {
dependsOn["mythic_postgres"] = map[string]string{
"condition": "service_healthy",
},
}
}
if len(dependsOn) > 0 {
pStruct["depends_on"] = dependsOn
} else {
delete(pStruct, "depends_on")
}
if mythicEnv.GetBool("hasura_use_build_context") {
pStruct["build"] = map[string]interface{}{
@@ -200,7 +190,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
pStruct["cpus"] = mythicEnv.GetInt("HASURA_CPUS")
@@ -208,8 +198,8 @@ func AddMythicService(service string, removeVolume bool) {
pStruct["mem_limit"] = mythicEnv.GetString("hasura_mem_limit")
}
environment := []string{
"HASURA_GRAPHQL_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}",
"HASURA_GRAPHQL_METADATA_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}",
"HASURA_GRAPHQL_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=${POSTGRES_SSLMODE:-disable}",
"HASURA_GRAPHQL_METADATA_DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@${POSTGRES_HOST}:${POSTGRES_PORT}/${POSTGRES_DB}?sslmode=${POSTGRES_SSLMODE:-disable}",
"HASURA_GRAPHQL_ENABLE_CONSOLE=true",
"HASURA_GRAPHQL_DEV_MODE=false",
"HASURA_GRAPHQL_ADMIN_SECRET=${HASURA_SECRET}",
@@ -239,16 +229,7 @@ func AddMythicService(service string, removeVolume bool) {
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
if mythicEnv.GetBool("hasura_bind_localhost_only") {
environment = append(environment, "HASURA_GRAPHQL_SERVER_HOST=127.0.0.1")
} else {
@@ -264,22 +245,36 @@ func AddMythicService(service string, removeVolume bool) {
delete(pStruct, "volumes")
}
case "mythic_nginx":
pStruct["depends_on"] = map[string]map[string]string{
"mythic_server": {
dependsOn := map[string]map[string]string{}
if isServiceInternal("mythic_server") {
dependsOn["mythic_server"] = map[string]string{
"condition": "service_healthy",
},
"mythic_react": {
}
}
if isServiceInternal("mythic_react") {
dependsOn["mythic_react"] = map[string]string{
"condition": "service_started",
},
"mythic_jupyter": {
}
}
if isServiceInternal("mythic_jupyter") {
dependsOn["mythic_jupyter"] = map[string]string{
"condition": "service_healthy",
},
"mythic_documentation": {
}
}
if isServiceInternal("mythic_documentation") {
dependsOn["mythic_documentation"] = map[string]string{
"condition": "service_started",
},
"mythic_graphql": {
}
}
if isServiceInternal("mythic_graphql") {
dependsOn["mythic_graphql"] = map[string]string{
"condition": "service_healthy",
},
}
}
if len(dependsOn) > 0 {
pStruct["depends_on"] = dependsOn
} else {
delete(pStruct, "depends_on")
}
if mythicEnv.GetBool("nginx_use_build_context") {
pStruct["build"] = map[string]interface{}{
@@ -288,7 +283,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
nginxUseSSL := "ssl"
@@ -348,16 +343,7 @@ func AddMythicService(service string, removeVolume bool) {
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
if mythicEnv.GetBool("nginx_bind_localhost_only") {
finalNginxEnv = append(finalNginxEnv, "NGINX_BIND_IPV4=127.0.0.1", "NGINX_BIND_IPV6=[::1]")
} else {
@@ -393,7 +379,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
pStruct["cpus"] = mythicEnv.GetInt("RABBITMQ_CPUS")
if mythicEnv.GetString("rabbitmq_mem_limit") != "" {
@@ -434,16 +420,7 @@ func AddMythicService(service string, removeVolume bool) {
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
if mythicEnv.GetBool("rabbitmq_bind_localhost_only") {
environment = append(environment, "RABBITMQ_NODE_IP_ADDRESS=127.0.0.1", "RABBITMQ_NODE_PORT=${RABBITMQ_PORT}")
} else {
@@ -469,16 +446,26 @@ func AddMythicService(service string, removeVolume bool) {
}
}
case "mythic_react":
pStruct["depends_on"] = map[string]map[string]string{
"mythic_server": {
dependsOn := map[string]map[string]string{}
if isServiceInternal("mythic_server") {
dependsOn["mythic_server"] = map[string]string{
"condition": "service_healthy",
},
"mythic_graphql": {
}
}
if isServiceInternal("mythic_graphql") {
dependsOn["mythic_graphql"] = map[string]string{
"condition": "service_healthy",
},
"mythic_postgres": {
}
}
if isServiceInternal("mythic_postgres") {
dependsOn["mythic_postgres"] = map[string]string{
"condition": "service_healthy",
},
}
}
if len(dependsOn) > 0 {
pStruct["depends_on"] = dependsOn
} else {
delete(pStruct, "depends_on")
}
if mythicEnv.GetBool("mythic_react_debug") {
pStruct["build"] = map[string]interface{}{
@@ -502,7 +489,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
if !mythicEnv.GetBool("mythic_react_use_volume") {
@@ -550,16 +537,7 @@ func AddMythicService(service string, removeVolume bool) {
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
if mythicEnv.GetBool("mythic_react_bind_localhost_only") {
pStruct["environment"] = []string{
"MYTHIC_REACT_PORT=${MYTHIC_REACT_PORT}",
@@ -574,16 +552,26 @@ func AddMythicService(service string, removeVolume bool) {
}
case "mythic_jupyter":
pStruct["depends_on"] = map[string]map[string]string{
"mythic_server": {
dependsOn := map[string]map[string]string{}
if isServiceInternal("mythic_server") {
dependsOn["mythic_server"] = map[string]string{
"condition": "service_healthy",
},
"mythic_postgres": {
}
}
if isServiceInternal("mythic_graphql") {
dependsOn["mythic_graphql"] = map[string]string{
"condition": "service_healthy",
},
"mythic_graphql": {
}
}
if isServiceInternal("mythic_postgres") {
dependsOn["mythic_postgres"] = map[string]string{
"condition": "service_healthy",
},
}
}
if len(dependsOn) > 0 {
pStruct["depends_on"] = dependsOn
} else {
delete(pStruct, "depends_on")
}
if mythicEnv.GetBool("jupyter_use_build_context") {
pStruct["build"] = map[string]interface{}{
@@ -592,7 +580,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
pStruct["cpus"] = mythicEnv.GetInt("JUPYTER_CPUS")
@@ -615,20 +603,12 @@ func AddMythicService(service string, removeVolume bool) {
"JUPYTER_TOKEN=${JUPYTER_TOKEN}",
"CHOWN_EXTRA=/projects",
"CHOWN_EXTRA_OPTS=-R",
"JUPYTER_PORT=${JUPYTER_PORT}",
}
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
environment := []string{
"JUPYTER_TOKEN=${JUPYTER_TOKEN}",
"CHOWN_EXTRA=/projects",
@@ -664,13 +644,21 @@ func AddMythicService(service string, removeVolume bool) {
}
}
case "mythic_server":
pStruct["depends_on"] = map[string]map[string]string{
"mythic_postgres": {
dependsOn := map[string]map[string]string{}
if isServiceInternal("mythic_rabbitmq") {
dependsOn["mythic_rabbitmq"] = map[string]string{
"condition": "service_healthy",
},
"mythic_rabbitmq": {
}
}
if isServiceInternal("mythic_postgres") {
dependsOn["mythic_postgres"] = map[string]string{
"condition": "service_healthy",
},
}
}
if len(dependsOn) > 0 {
pStruct["depends_on"] = dependsOn
} else {
delete(pStruct, "depends_on")
}
if mythicEnv.GetBool("mythic_server_use_build_context") {
pStruct["build"] = map[string]interface{}{
@@ -679,7 +667,7 @@ func AddMythicService(service string, removeVolume bool) {
}
pStruct["image"] = service
} else {
pStruct["image"] = fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest"))
pStruct["image"] = applyImageMirror(fmt.Sprintf("ghcr.io/its-a-feature/%s:%s", service, mythicEnv.GetString("global_docker_latest")), mythicEnv.GetString("mythic_docker_image_mirror"))
}
pStruct["cpus"] = mythicEnv.GetInt("MYTHIC_SERVER_CPUS")
@@ -709,6 +697,7 @@ func AddMythicService(service string, removeVolume bool) {
"POSTGRES_HOST=${POSTGRES_HOST}",
"POSTGRES_PORT=${POSTGRES_PORT}",
"POSTGRES_PASSWORD=${POSTGRES_PASSWORD}",
"POSTGRES_SSLMODE=${POSTGRES_SSLMODE}",
"RABBITMQ_HOST=${RABBITMQ_HOST}",
"RABBITMQ_PORT=${RABBITMQ_PORT}",
"RABBITMQ_PASSWORD=${RABBITMQ_PASSWORD}",
@@ -739,16 +728,7 @@ func AddMythicService(service string, removeVolume bool) {
} else {
delete(pStruct, "ports")
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
}
if _, ok := pStruct["environment"]; ok {
@@ -773,10 +753,12 @@ func AddMythicService(service string, removeVolume bool) {
}
}
case "mythic_sync":
pStruct["depends_on"] = map[string]map[string]string{
"mythic_nginx": {
"condition": "service_healthy",
},
if isServiceInternal("mythic_nginx") {
pStruct["depends_on"] = map[string]map[string]string{
"mythic_nginx": {"condition": "service_healthy"},
}
} else {
delete(pStruct, "depends_on")
}
absPath, err := filepath.Abs(filepath.Join(manager.GetManager().GetPathTo3rdPartyServicesOnDisk(), service))
if err != nil {
@@ -821,16 +803,7 @@ func AddMythicService(service string, removeVolume bool) {
delete(pStruct, "extra_hosts")
} else {
pStruct["network_mode"] = "host"
pStruct["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
}
pStruct["extra_hosts"] = getExtraHosts()
}
}
@@ -862,29 +835,25 @@ func Add3rdPartyService(service string, additionalConfigs map[string]interface{}
"args": config.GetBuildArguments(),
}
existingConfig["network_mode"] = "host"
existingConfig["extra_hosts"] = []string{
"mythic_server:127.0.0.1",
"mythic_rabbitmq:127.0.0.1",
"mythic_nginx:127.0.0.1",
"mythic_react:127.0.0.1",
"mythic_documentation:127.0.0.1",
"mythic_graphql:127.0.0.1",
"mythic_jupyter:127.0.0.1",
"mythic_postgres:127.0.0.1",
existingConfig["extra_hosts"] = getExtraHosts()
dependsOn := map[string]map[string]string{}
if isServiceInternal("mythic_server") {
dependsOn["mythic_server"] = map[string]string{"condition": "service_healthy"}
}
existingConfig["depends_on"] = map[string]map[string]string{
"mythic_server": {
"condition": "service_healthy",
},
"mythic_rabbitmq": {
"condition": "service_healthy",
},
"mythic_nginx": {
"condition": "service_healthy",
},
"mythic_graphql": {
"condition": "service_healthy",
},
if isServiceInternal("mythic_rabbitmq") {
dependsOn["mythic_rabbitmq"] = map[string]string{"condition": "service_healthy"}
}
if isServiceInternal("mythic_graphql") {
dependsOn["mythic_graphql"] = map[string]string{"condition": "service_healthy"}
}
if isServiceInternal("mythic_nginx") {
dependsOn["mythic_nginx"] = map[string]string{"condition": "service_healthy"}
}
if len(dependsOn) > 0 {
existingConfig["depends_on"] = dependsOn
} else {
delete(existingConfig, "depends_on")
}
agentConfigs := config.GetConfigStrings([]string{fmt.Sprintf("%s_.*", service)})
agentUseBuildContextKey := fmt.Sprintf("%s_use_build_context", service)
@@ -893,7 +862,7 @@ func Add3rdPartyService(service string, additionalConfigs map[string]interface{}
if useBuildContext, ok := agentConfigs[agentUseBuildContextKey]; ok {
if useBuildContext == "false" {
delete(existingConfig, "build")
existingConfig["image"] = agentConfigs[agentRemoteImageKey]
existingConfig["image"] = applyImageMirror(agentConfigs[agentRemoteImageKey], config.GetMythicEnv().GetString("mythic_docker_image_mirror"))
}
}
if useVolume, ok := agentConfigs[agentUseVolumeKey]; ok {
@@ -983,3 +952,36 @@ func Initialize() {
}
}
}
// applyImageMirror rewrites imageURL to be served from mirror by stripping the
// upstream registry host (the first '/'-separated segment, when it contains '.'
// or ':' or equals 'localhost' per Docker's reference parsing) and prepending
// mirror. An empty mirror returns imageURL unchanged.
func applyImageMirror(imageURL, mirror string) string {
mirror = strings.TrimSpace(mirror)
mirror = strings.TrimRight(mirror, "/")
if mirror == "" {
return imageURL
}
if idx := strings.Index(imageURL, "/"); idx > 0 {
first := imageURL[:idx]
if strings.ContainsAny(first, ".:") || first == "localhost" {
return mirror + "/" + imageURL[idx+1:]
}
}
return mirror + "/" + imageURL
}
func isServiceInternal(service string) bool {
return manager.GetManager().IsServiceInternal(service)
}
func getExtraHosts() []string {
extraHosts := []string{}
for _, service := range config.MythicPossibleServices {
if isServiceInternal(service) {
extraHosts = append(extraHosts, fmt.Sprintf("%s:127.0.0.1", service))
}
}
return extraHosts
}
@@ -156,6 +156,10 @@ func Status(verbose bool) {
log.Printf("\n[*] MythicServer is currently listening on localhost. If you have a remote Service, they will be unable to connect (i.e. one running on another server)")
log.Printf("\n Use 'sudo ./mythic-cli config set mythic_server_bind_localhost_only false' and restart mythic ('sudo ./mythic-cli restart') to change this\n")
}
if mirror := mythicEnv.GetString("mythic_docker_image_mirror"); mirror != "" {
log.Printf("\n[*] Image mirror is active: pulling all images from \"%s\" instead of their upstream registries.", mirror)
log.Printf("\n Use 'sudo ./mythic-cli config set mythic_docker_image_mirror \"\"' and restart mythic ('sudo ./mythic-cli restart') to disable.\n")
}
log.Printf("[*] If you are using a remote PayloadType or C2Profile, they will need certain environment variables to properly connect to Mythic.\n")
log.Printf(" Use 'sudo ./mythic-cli config service' for configs for these services.\n")
}
+3
View File
@@ -112,6 +112,9 @@ func updateNginxBlockLists() {
outputString += "deny all;"
if !config.GetMythicEnv().GetBool("nginx_use_volume") {
ipFilePath := filepath.Join(utils.GetCwdFromExe(), "nginx-docker", "config", "blockips.conf")
if err := os.MkdirAll(filepath.Dir(ipFilePath), 0755); err != nil {
log.Fatalf("[-] Failed to create nginx-docker/config directory for block list file: %v\n", err)
}
if err := os.WriteFile(ipFilePath, []byte(outputString), 0600); err != nil {
log.Fatalf("[-] Failed to write out block list file: %v\n", err)
}
@@ -92,6 +92,18 @@ func (d *DockerComposeManager) IsServiceRunning(service string) bool {
return false
}
// IsServiceInternal checks if a service is configured to run locally in docker vs externally
func (d *DockerComposeManager) IsServiceInternal(service string) bool {
if !utils.StringInSlice(service, config.MythicPossibleServices) {
return true
}
intendedServices, err := config.GetIntendedMythicServiceNames()
if err != nil {
return true
}
return utils.StringInSlice(service, intendedServices)
}
// DoesImageExist use Docker API to check existing images for the specified name
func (d *DockerComposeManager) DoesImageExist(service string) bool {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
@@ -225,10 +237,11 @@ func (d *DockerComposeManager) LoadImages(outputPath string) error {
if err != nil {
return errors.New(fmt.Sprintf("[-] Failed to read tar file: %v\n", err))
}
_, err = cli.ImageLoad(context.Background(), ioReadCloser, false)
imageLoadResp, err := cli.ImageLoad(context.Background(), ioReadCloser, nil)
if err != nil {
return errors.New(fmt.Sprintf("[-] Failed to load image into Docker: %v\n", err))
}
imageLoadResp.Body.Close()
log.Printf("[+] loaded docker images!\n")
return nil
@@ -623,9 +636,9 @@ func (d *DockerComposeManager) TestPorts(services []string) {
var removeServices []string
mythicEnv := config.GetMythicEnv()
for key, val := range portChecks {
// only check ports for services we're about to start
// only check ports for services we're about to start that are running locally
if utils.StringInSlice(val[1], services) {
if mythicEnv.GetString(key) == val[1] || mythicEnv.GetString(key) == "127.0.0.1" {
if d.IsServiceInternal(val[1]) {
addServices = append(addServices, val[1])
p, err := net.Listen("tcp", ":"+strconv.Itoa(mythicEnv.GetInt(val[0])))
if err != nil {
@@ -958,7 +971,7 @@ func (d *DockerComposeManager) BackupDatabase(backupPath string, useVolume bool)
return err
}
defer cli.Close()
execID, err := cli.ContainerExecCreate(ctx, "mythic_postgres", types.ExecConfig{
execID, err := cli.ContainerExecCreate(ctx, "mythic_postgres", container.ExecOptions{
AttachStderr: true,
AttachStdout: true,
AttachStdin: true,
@@ -969,7 +982,7 @@ func (d *DockerComposeManager) BackupDatabase(backupPath string, useVolume bool)
} else {
log.Printf("[*] Created docker exec session")
}
session, err := cli.ContainerExecAttach(ctx, execID.ID, types.ExecStartCheck{})
session, err := cli.ContainerExecAttach(ctx, execID.ID, container.ExecStartOptions{})
if err != nil {
log.Fatalf("[!] Failed to attach to exec session: %v", err)
} else {
@@ -1034,7 +1047,7 @@ func (d *DockerComposeManager) RestoreDatabase(backupPath string, useVolume bool
return err
}
defer cli.Close()
execID, err := cli.ContainerExecCreate(ctx, "mythic_postgres", types.ExecConfig{
execID, err := cli.ContainerExecCreate(ctx, "mythic_postgres", container.ExecOptions{
AttachStderr: true,
AttachStdout: true,
AttachStdin: true,
@@ -1045,7 +1058,7 @@ func (d *DockerComposeManager) RestoreDatabase(backupPath string, useVolume bool
} else {
log.Printf("[*] Created docker exec session")
}
session, err := cli.ContainerExecAttach(ctx, execID.ID, types.ExecStartCheck{})
session, err := cli.ContainerExecAttach(ctx, execID.ID, container.ExecStartOptions{})
if err != nil {
log.Fatalf("[!] Failed to attach to exec session: %v", err)
} else {
@@ -1412,7 +1425,7 @@ func (d *DockerComposeManager) runDockerCompose(args []string) error {
return err
}
if processState.ExitCode() != 0 {
return err
return fmt.Errorf("docker-compose %v exited with code %d", args, processState.ExitCode())
}
}
return nil
@@ -12,6 +12,8 @@ type CLIManager interface {
GetManagerName() string
// IsServiceRunning checks if a service by the specified name is currently running or not
IsServiceRunning(service string) bool
// IsServiceInternal checks if a service is configured to run locally in docker vs externally
IsServiceInternal(service string) bool
// CheckRequiredManagerVersion checks if the version of the management software installed is a valid version or not
CheckRequiredManagerVersion() bool
// GenerateRequiredConfig creates any necessary base configuration files needed by the manager, like a docker-compose.yml file
+3 -1
View File
@@ -1,6 +1,8 @@
package cmd
import (
"log"
"github.com/MythicMeta/Mythic_CLI/cmd/config"
"github.com/MythicMeta/Mythic_CLI/cmd/internal"
"github.com/spf13/cobra"
@@ -31,6 +33,6 @@ func start(cmd *cobra.Command, args []string) {
keepVolume = !config.GetMythicEnv().GetBool("REBUILD_ON_START")
}
if err := internal.ServiceStart(args, localKeepVolume); err != nil {
log.Fatalf("[-] Failed to start Mythic: %v\n", err)
}
}
+3
View File
@@ -65,6 +65,9 @@ func init() {
}
func updateCheck(cmd *cobra.Command, args []string) {
if mirror := config.GetMythicEnv().GetString("mythic_docker_image_mirror"); mirror != "" {
log.Printf("[*] mythic_docker_image_mirror is set to %q; image URLs printed below are the upstream sources used for version checking, not the mirrored locations actually pulled by docker compose\n", mirror)
}
var tr = &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
+33 -35
View File
@@ -1,59 +1,57 @@
module github.com/MythicMeta/Mythic_CLI
go 1.21.0
toolchain go1.22.2
go 1.25.0
require (
github.com/creack/pty v1.1.23
github.com/docker/docker v27.2.0+incompatible
github.com/creack/pty v1.1.24
github.com/docker/docker v28.5.2+incompatible
github.com/rabbitmq/amqp091-go v1.10.0
github.com/spf13/cobra v1.8.1
github.com/spf13/viper v1.19.0
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948
golang.org/x/mod v0.20.0
github.com/spf13/cobra v1.10.2
github.com/spf13/viper v1.21.0
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90
golang.org/x/mod v0.34.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/sys/atomicwriter v0.1.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/sagikazarmark/locafero v0.6.0 // indirect
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/spf13/cast v1.7.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/sdk v1.25.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/sys v0.24.0 // indirect
golang.org/x/text v0.17.0 // indirect
golang.org/x/tools v0.24.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
go.opentelemetry.io/otel v1.43.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect
go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/otel/sdk v1.43.0 // indirect
go.opentelemetry.io/otel/sdk/metric v1.43.0 // indirect
go.opentelemetry.io/otel/trace v1.43.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
golang.org/x/time v0.15.0 // indirect
gotest.tools/v3 v3.5.1 // indirect
)
+100 -186
View File
@@ -1,232 +1,146 @@
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0=
github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/creack/pty v1.1.23 h1:4M6+isWdcStXEf15G/RbrMPOQj1dZ7HPZCGwE4kOeP0=
github.com/creack/pty v1.1.23/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker/docker v26.0.2+incompatible h1:yGVmKUFGgcxA6PXWAokO0sQL22BrQ67cgVjko8tGdXE=
github.com/docker/docker v26.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/docker v27.2.0+incompatible h1:Rk9nIVdfH3+Vz4cyI/uhbINhEZ/oLmc+CBXmH6fbNk4=
github.com/docker/docker v27.2.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0 h1:Wqo399gCIufwto+VfwCSvsnfGpF/w5E9CNxSwbpD6No=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.0/go.mod h1:qmOFXW2epJhM0qSnUUYpldc7gVz2KMQwJ/QYCDIa7XU=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rabbitmq/amqp091-go v1.9.0 h1:qrQtyzB4H8BQgEuJwhmVQqVHB9O4+MNDJCCAcpc3Aoo=
github.com/rabbitmq/amqp091-go v1.9.0/go.mod h1:+jPrT9iY2eLjRaMSRHUhc3z14E/l85kv/f+6luSD3pc=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
github.com/sagikazarmark/locafero v0.6.0 h1:ON7AQg37yzcRPU69mt7gwhFEBwxI6P9T4Qu3N51bwOk=
github.com/sagikazarmark/locafero v0.6.0/go.mod h1:77OmuIc6VTraTXKXIs/uvUxKGUXjE1GbemJYHqdNjX0=
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
github.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=
github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=
github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0 h1:dT33yIHtmsqpixFsSQPwNeY5drM9wTcoL8h0FWF4oGM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.25.0/go.mod h1:h95q0LBGh7hlAC08X2DhSeyIG02YQ0UyioTCVAqRPmc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0 h1:Mbi5PKN7u322woPa85d7ebZ+SOvEoPvoiBu+ryHWgfA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.25.0/go.mod h1:e7ciERRhZaOZXVjx5MiL8TK5+Xv7G5Gv5PA2ZDEJdL8=
go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo=
go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw=
go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/proto/otlp v1.1.0 h1:2Di21piLrCqJ3U3eXGCTPHE9R8Nh+0uglSnOyxikMeI=
go.opentelemetry.io/proto/otlp v1.1.0/go.mod h1:GpBHCBWiqvVLDqmHZsoMM3C5ySeKTC7ej/RNTae6MdY=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc=
golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 h1:kx6Ds3MlpiUHKj7syVnbp57++8WpuKPcR5yjLBjvLEA=
golang.org/x/exp v0.0.0-20240823005443-9b4947da3948/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=
golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.19.0 h1:tfGCXNR1OsFG+sVdLAitlpjAvD/I6dHDKnYrpEZUHkw=
golang.org/x/tools v0.19.0/go.mod h1:qoJWxmGSIBmAeriMx19ogtrEPrGtDbPK634QFIcLAhc=
golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=
golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de h1:jFNzHPIeuzhdRwVhbZdiym9q0ory/xY3sA+v2wPg8I0=
google.golang.org/genproto/googleapis/api v0.0.0-20240227224415-6ceb2ff114de/go.mod h1:5iCWqnniDlqZHrd3neWVTOwvh/v6s3232omMecelax8=
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.63.0 h1:WjKe+dnvABXyPJMD7KDNLxtoGk5tgk+YFWN6cBWjZE8=
google.golang.org/grpc v1.63.0/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
+1 -1
View File
@@ -1 +1 @@
3.4.25
3.4.36
@@ -24,12 +24,9 @@ select_permissions:
- end_time
- start_time
filter:
_or:
- payloadtype_id:
_is_null: false
- payload:
operation_id:
_in: X-Hasura-operations
- payload:
operation_id:
_eq: X-Hasura-current-operation-id
- role: mythic_admin
permission:
columns:
@@ -45,12 +42,9 @@ select_permissions:
- end_time
- start_time
filter:
_or:
- payloadtype_id:
_is_null: false
- payload:
operation_id:
_in: X-Hasura-operations
- payload:
operation_id:
_eq: X-Hasura-current-operation-id
- role: operation_admin
permission:
columns:
@@ -66,12 +60,9 @@ select_permissions:
- end_time
- start_time
filter:
_or:
- payloadtype_id:
_is_null: false
- payload:
operation_id:
_in: X-Hasura-operations
- payload:
operation_id:
_eq: X-Hasura-current-operation-id
- role: operator
permission:
columns:
@@ -87,12 +78,9 @@ select_permissions:
- end_time
- start_time
filter:
_or:
- payloadtype_id:
_is_null: false
- payload:
operation_id:
_in: X-Hasura-operations
- payload:
operation_id:
_eq: X-Hasura-current-operation-id
- role: spectator
permission:
columns:
@@ -108,9 +96,6 @@ select_permissions:
- end_time
- start_time
filter:
_or:
- payloadtype_id:
_is_null: false
- payload:
operation_id:
_in: X-Hasura-operations
- payload:
operation_id:
_eq: X-Hasura-current-operation-id
+80 -75
View File
@@ -4307,34 +4307,35 @@
- role: developer
permission:
columns:
- size
- complete
- delete_after_fetch
- deleted
- is_download_from_agent
- is_payload
- is_screenshot
- filename
- full_remote_path
- agent_file_id
- apitokens_id
- chunk_size
- chunks_received
- comment
- complete
- copy_of_file_id
- delete_after_fetch
- deleted
- eventgroup_id
- eventstepinstance_id
- filename
- full_remote_path
- host
- id
- is_download_from_agent
- is_payload
- is_screenshot
- md5
- mythictree_id
- operation_id
- operator_id
- task_id
- total_chunks
- agent_file_id
- comment
- host
- md5
- path
- received_chunk_ids
- sha1
- size
- task_id
- timestamp
- total_chunks
computed_fields:
- filename_text
- filename_utf8
@@ -4347,34 +4348,35 @@
- role: mythic_admin
permission:
columns:
- size
- complete
- delete_after_fetch
- deleted
- is_download_from_agent
- is_payload
- is_screenshot
- filename
- full_remote_path
- agent_file_id
- apitokens_id
- chunk_size
- chunks_received
- comment
- complete
- copy_of_file_id
- delete_after_fetch
- deleted
- eventgroup_id
- eventstepinstance_id
- filename
- full_remote_path
- host
- id
- is_download_from_agent
- is_payload
- is_screenshot
- md5
- mythictree_id
- operation_id
- operator_id
- task_id
- total_chunks
- agent_file_id
- comment
- host
- md5
- path
- received_chunk_ids
- sha1
- size
- task_id
- timestamp
- total_chunks
computed_fields:
- filename_text
- filename_utf8
@@ -4387,34 +4389,35 @@
- role: operation_admin
permission:
columns:
- size
- complete
- delete_after_fetch
- deleted
- is_download_from_agent
- is_payload
- is_screenshot
- filename
- full_remote_path
- agent_file_id
- apitokens_id
- chunk_size
- chunks_received
- comment
- complete
- copy_of_file_id
- delete_after_fetch
- deleted
- eventgroup_id
- eventstepinstance_id
- filename
- full_remote_path
- host
- id
- is_download_from_agent
- is_payload
- is_screenshot
- md5
- mythictree_id
- operation_id
- operator_id
- task_id
- total_chunks
- agent_file_id
- comment
- host
- md5
- path
- received_chunk_ids
- sha1
- size
- task_id
- timestamp
- total_chunks
computed_fields:
- filename_text
- filename_utf8
@@ -4427,34 +4430,35 @@
- role: operator
permission:
columns:
- size
- complete
- delete_after_fetch
- deleted
- is_download_from_agent
- is_payload
- is_screenshot
- filename
- full_remote_path
- agent_file_id
- apitokens_id
- chunk_size
- chunks_received
- comment
- complete
- copy_of_file_id
- delete_after_fetch
- deleted
- eventgroup_id
- eventstepinstance_id
- filename
- full_remote_path
- host
- id
- is_download_from_agent
- is_payload
- is_screenshot
- md5
- mythictree_id
- operation_id
- operator_id
- task_id
- total_chunks
- agent_file_id
- comment
- host
- md5
- path
- received_chunk_ids
- sha1
- size
- task_id
- timestamp
- total_chunks
computed_fields:
- filename_text
- filename_utf8
@@ -4467,34 +4471,35 @@
- role: spectator
permission:
columns:
- size
- complete
- delete_after_fetch
- deleted
- is_download_from_agent
- is_payload
- is_screenshot
- filename
- full_remote_path
- agent_file_id
- apitokens_id
- chunk_size
- chunks_received
- comment
- complete
- copy_of_file_id
- delete_after_fetch
- deleted
- eventgroup_id
- eventstepinstance_id
- filename
- full_remote_path
- host
- id
- is_download_from_agent
- is_payload
- is_screenshot
- md5
- mythictree_id
- operation_id
- operator_id
- task_id
- total_chunks
- agent_file_id
- comment
- host
- md5
- path
- received_chunk_ids
- sha1
- size
- task_id
- timestamp
- total_chunks
computed_fields:
- filename_text
- filename_utf8
+9 -3
View File
@@ -15,7 +15,7 @@ import (
)
var DB *sqlx.DB
var currentMigrationVersion int64 = 3003017
var currentMigrationVersion int64 = 3003018
// initial structs made with './tables-to-go -u mythic_user -p [password here] -h [ip here] -v -d mythic_db -of output -pn database_structs'
// package pulled from https://github.com/fraenky8/tables-to-go
@@ -36,6 +36,11 @@ func Initialize() {
logging.LogInfo("Disconnecting from database and reconnecting to load new schema")
DB.Close()
DB = getNewDbConnection()
if utils.MythicConfig.PostgresDebug {
if _, err := DB.Exec("CREATE EXTENSION IF NOT EXISTS pg_stat_statements SCHEMA public;"); err != nil {
logging.LogWarning("Failed to create pg_stat_statements extension (requires superuser)", "error", err.Error())
}
}
// we need to initialize the admin user and operation
salt := uuid.NewString()
newUser := databaseStructs.Operator{
@@ -161,12 +166,13 @@ func checkDBConnection() {
func getNewDbConnection() *sqlx.DB {
for {
logging.LogInfo("Attempting to connect to database...", "host", utils.MythicConfig.PostgresHost, "port", utils.MythicConfig.PostgresPort)
conn, err := sqlx.Connect("postgres", fmt.Sprintf("user='%s' password='%s' host='%s' port='%d' dbname='%s' sslmode=disable connect_timeout=10",
conn, err := sqlx.Connect("postgres", fmt.Sprintf("user='%s' password='%s' host='%s' port='%d' dbname='%s' sslmode=%s connect_timeout=10",
utils.MythicConfig.PostgresUser,
utils.MythicConfig.PostgresPassword,
utils.MythicConfig.PostgresHost,
utils.MythicConfig.PostgresPort,
utils.MythicConfig.PostgresDB),
utils.MythicConfig.PostgresDB,
utils.MythicConfig.PostgresSSLMode),
)
if err != nil {
logging.LogError(err, "Failed to connect to database", "host", utils.MythicConfig.PostgresHost, "port", utils.MythicConfig.PostgresPort)
@@ -0,0 +1,9 @@
-- +migrate Up
-- SQL in section 'Up' is executed when this migration is applied
alter table "public"."filemeta" add column IF NOT EXISTS "received_chunk_ids" jsonb not null default jsonb_build_object();
-- +migrate Down
-- SQL in section 'Down' is executed when this migration is rolled back
-2
View File
@@ -4847,8 +4847,6 @@ ALTER TABLE ONLY public.wrappedpayloadtypes
ALTER TABLE ONLY public.wrappedpayloadtypes
ADD CONSTRAINT wrappedpayloadtypes_wrapper_id_fkey FOREIGN KEY (wrapper_id) REFERENCES public.payloadtype(id) ON DELETE CASCADE;
CREATE EXTENSION pg_stat_statements SCHEMA public;
--
-- PostgreSQL database dump complete
--
@@ -38,4 +38,5 @@ type Filemeta struct {
EventStepInstanceID structs.NullInt64 `db:"eventstepinstance_id" json:"event_step_instance_id" mapstructure:"event_step_instance_id"`
APITokensID structs.NullInt64 `db:"apitokens_id" json:"api_tokens_id" mapstructure:"apitokens_id"`
CopyOfFileID structs.NullInt64 `db:"copy_of_file_id" json:"copy_of_file_id" mapstructure:"copy_of_file_id"`
ReceivedChunkIDs MythicJSONText `db:"received_chunk_ids" json:"received_chunk_ids" mapstructure:"received_chunk_id"`
}
@@ -85,6 +85,17 @@ func (j MythicJSONText) StructValue() map[string]interface{} {
}
return newMap
}
func (j MythicJSONText) StructValueMapInt() map[int]interface{} {
newMap := make(map[int]interface{})
if err := j.Unmarshal(&newMap); err != nil {
logging.LogError(err, "Failed to unmarshal types.JSONText into map[int]interface{}")
return newMap
}
if newMap == nil {
newMap = make(map[int]interface{})
}
return newMap
}
func (j MythicJSONArray) StructValue() []interface{} {
newArray := []interface{}{}
if err := j.Unmarshal(&newArray); err != nil {
-29
View File
@@ -165,35 +165,6 @@ func HashUserPassword(databaseOperator databaseStructs.Operator, password string
return fmt.Sprintf("%x", hashBytes)
}
func ResolveAllOperationsMessage(message string, operationID int) {
/*
Resolve a message in all operation's event logs if operationID is 0, otherwise just resolve it to the specific operation.
*/
operations := []databaseStructs.Operation{}
if err := DB.Select(&operations, `SELECT id FROM operation WHERE complete=false`); err != nil {
logging.LogError(err, "Failed to get operations for ResolveAllOperationsMessage", "data", message)
return
}
for _, operation := range operations {
if operationID == 0 || operation.ID == operationID {
// this is the operation we're interested in
updateObject := databaseStructs.Operationeventlog{
Message: message,
OperationID: operationID,
Level: MESSAGE_LEVEL_INFO,
}
if operationID == 0 {
updateObject.OperationID = operation.ID
}
if _, err := DB.NamedExec(`UPDATE operationeventlog SET
resolved=true
WHERE warning=true AND resolved=false AND deleted=false AND message=:message AND operation_id=:operation_id`, updateObject); err != nil {
logging.LogError(err, "Failed to resolve message")
}
}
}
}
func AssignNewOperatorAllBrowserScripts(userID int) {
browserscripts := []databaseStructs.Browserscript{}
if err := DB.Select(&browserscripts, `SELECT
+21 -21
View File
@@ -3,40 +3,40 @@ module github.com/its-a-feature/Mythic
go 1.25.1
require (
github.com/gin-gonic/gin v1.11.0
github.com/go-co-op/gocron/v2 v2.19.0
github.com/go-viper/mapstructure/v2 v2.4.0
github.com/gin-gonic/gin v1.12.0
github.com/go-co-op/gocron/v2 v2.19.1
github.com/go-viper/mapstructure/v2 v2.5.0
github.com/golang-jwt/jwt v3.2.2+incompatible
github.com/google/uuid v1.6.0
github.com/jmoiron/sqlx v1.4.0
github.com/lib/pq v1.10.9
github.com/lib/pq v1.11.2
github.com/mitchellh/mapstructure v1.5.0
github.com/pelletier/go-toml/v2 v2.2.4
github.com/rabbitmq/amqp091-go v1.10.0
github.com/rs/zerolog v1.34.0
github.com/rubenv/sql-migrate v1.8.1
github.com/spf13/viper v1.21.0
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93
golang.org/x/mod v0.31.0
google.golang.org/grpc v1.78.0
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90
golang.org/x/mod v0.34.0
google.golang.org/grpc v1.79.3
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/bytedance/gopkg v0.1.3 // indirect
github.com/bytedance/sonic v1.14.2 // indirect
github.com/bytedance/sonic/loader v0.4.0 // indirect
github.com/bytedance/sonic v1.15.0 // indirect
github.com/bytedance/sonic/loader v0.5.0 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.12 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.30.0 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.19.1 // indirect
github.com/go-playground/validator/v10 v10.30.1 // indirect
github.com/goccy/go-json v0.10.6 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
@@ -46,7 +46,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/quic-go/qpack v0.6.0 // indirect
github.com/quic-go/quic-go v0.58.0 // indirect
github.com/quic-go/quic-go v0.59.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/sagikazarmark/locafero v0.12.0 // indirect
github.com/spf13/afero v1.15.0 // indirect
@@ -55,12 +55,12 @@ require (
github.com/subosito/gotenv v1.6.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
go.uber.org/mock v0.6.0 // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/arch v0.23.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/net v0.48.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
golang.org/x/arch v0.25.0 // indirect
golang.org/x/crypto v0.49.0 // indirect
golang.org/x/net v0.52.0 // indirect
golang.org/x/sys v0.42.0 // indirect
golang.org/x/text v0.35.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 // indirect
)
+54 -49
View File
@@ -2,10 +2,12 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE=
github.com/bytedance/sonic v1.14.2/go.mod h1:T80iDELeHiHKSc0C9tubFygiuXoGzrkjKzX2quAx980=
github.com/bytedance/sonic/loader v0.4.0 h1:olZ7lEqcxtZygCK9EKYKADnpQoYkRQxaeY2NYzevs+o=
github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
@@ -16,14 +18,14 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
github.com/go-co-op/gocron/v2 v2.19.0 h1:OKf2y6LXPs/BgBI2fl8PxUpNAI1DA9Mg+hSeGOS38OU=
github.com/go-co-op/gocron/v2 v2.19.0/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-co-op/gocron/v2 v2.19.1 h1:B4iLeA0NB/2iO3EKQ7NfKn5KsQgZfjb2fkvoZJU3yBI=
github.com/go-co-op/gocron/v2 v2.19.1/go.mod h1:5lEiCKk1oVJV39Zg7/YG10OnaVrDAV5GGR6O0663k6U=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
@@ -36,16 +38,16 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.0 h1:5YBPNs273uzsZJD1I8uiB4Aqg9sN6sMDVX3s6LxmhWU=
github.com/go-playground/validator/v10 v10.30.0/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.1 h1:3rG3+v8pkhRqoQ/88NYNMHYVGYztCOCIZ7UQhu7H+NE=
github.com/goccy/go-yaml v1.19.1/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
@@ -70,8 +72,9 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs=
github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
@@ -97,8 +100,8 @@ github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY=
github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.58.0 h1:ggY2pvZaVdB9EyojxL1p+5mptkuHyX5MOSv4dgWF4Ug=
github.com/quic-go/quic-go v0.58.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw=
github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o=
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
@@ -137,47 +140,49 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48=
go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8=
go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0=
go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs=
go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18=
go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE=
go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8=
go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew=
go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI=
go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg=
golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI=
golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/arch v0.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE=
golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA=
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ=
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc=
google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5 h1:aJmi6DVGGIStN9Mobk/tZOOQUBbj0BPjZjjnOdoZKts=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -300,7 +300,7 @@ func checkContainerStatus() {
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
go updateDownContainerBuildingPayloads(container)
} else {
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
}
} else {
@@ -332,7 +332,7 @@ func checkContainerStatus() {
getDownContainerMessage(container),
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
} else {
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
}
} else {
@@ -363,7 +363,7 @@ func checkContainerStatus() {
getDownContainerMessage(container),
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
} else {
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
}
} else {
@@ -395,7 +395,7 @@ func checkContainerStatus() {
getDownContainerMessage(container),
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
} else {
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
}
} else {
@@ -426,7 +426,7 @@ func checkContainerStatus() {
getDownContainerMessage(container),
0, fmt.Sprintf("%s_container_down", container), database.MESSAGE_LEVEL_INFO, true)
} else {
go database.ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(container), 0)
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(container)
}
} else {
+6 -5
View File
@@ -86,8 +86,9 @@ var pushC2StreamingDisconnectNotification = make(chan int, 100)
func Initialize() {
RabbitMQConnection.channelMutexMap = make(map[string]*channelMutex)
go invalidateAllSpectatorAPITokens()
go InvalidateOperationEventLogCacheMap()
invalidateAllNonUserAPITokens()
InvalidateOperationEventLogCacheMap()
go listenForOperationsMessages()
go listenForWriteDownloadChunkToLocalDisk()
go listenForFileBrowserData()
go listenForAsyncAgentMessagePostResponseContent()
@@ -109,10 +110,10 @@ func Initialize() {
go listenForEvents()
go initializeEventGroupCronSchedulesOnStart()
// start listening for new messages from push c2 profiles, needs gRPC initialized first
go listenForPushConnectDisconnectMessages()
go processAgentMessageFromPushC2()
go interceptProxyDataToAgentForPushC2()
go checkIfActiveCallbacksAreAliveForever()
go listenForPushConnectDisconnectMessages()
go func() {
// wait 20s for things to stabilize a bit, then send a startup message
time.Sleep(time.Second * 30)
@@ -171,9 +172,9 @@ func emitStartupMessages() {
}
func invalidateAllSpectatorAPITokens() {
func invalidateAllNonUserAPITokens() {
_, err := database.DB.Exec(`UPDATE apitokens
SET deleted=true, active=true WHERE token_type=$1 OR token_type=$2 OR token_type=$3`,
SET deleted=true, active=false WHERE token_type=$1 OR token_type=$2 OR token_type=$3`,
mythicjwt.AUTH_METHOD_GRAPHQL_SPECTATOR, mythicjwt.AUTH_METHOD_TASK, mythicjwt.AUTH_METHOD_EVENT)
if err != nil {
logging.LogError(err, "failed to mark all spectator tokens as deleted and inactive")
+2 -1
View File
@@ -191,7 +191,7 @@ func c2Sync(in C2SyncMessage) error {
return err
}
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s", c2Profile.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
go database.ResolveAllOperationsMessage(getDownContainerMessage(c2Profile.Name), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(c2Profile.Name), 0)
go autoStartC2Profile(c2Profile, false)
if newProfile {
go reSyncPayloadTypes()
@@ -251,6 +251,7 @@ func updateC2Parameters(in C2SyncMessage, c2Profile databaseStructs.C2profile) e
logging.LogError(err, "Failed to fetch c2 parameters for c2 profile when syncing")
return err
} else {
defer rows.Close()
for rows.Next() {
found := false
if err = rows.StructScan(&databaseParameter); err != nil {
@@ -134,7 +134,7 @@ func consumingServicesSync(in ConsumingContainerSyncMessage) error {
}
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s",
consumingContainer.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
go database.ResolveAllOperationsMessage(getDownContainerMessage(consumingContainer.Name), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(consumingContainer.Name), 0)
checkContainerStatusAddConsumingContainerChannel <- consumingContainer
// update eventgroup consumingcontainer mappings
eventing.UpdateEventGroupConsumingContainersMappingByConsumingContainer(consumingContainer)
@@ -190,7 +190,7 @@ func customBrowserSync(in CustomBrowserSyncMessage) error {
return err
}
go SendAllOperationsMessage(fmt.Sprintf("Successfully synced %s with container version %s", customSyncBrowser.Name, in.ContainerVersion), 0, "debug", database.MESSAGE_LEVEL_DEBUG, false)
go database.ResolveAllOperationsMessage(getDownContainerMessage(customSyncBrowser.Name), 0)
go ResolveAllOperationsMessage(getDownContainerMessage(customSyncBrowser.Name), 0)
checkContainerStatusAddCustomBrowserChannel <- customSyncBrowser
go CreateGraphQLSpectatorAPITokenAndSendOnStartMessage(customSyncBrowser.Name)
Loaded 100 of 137 files, more files were not shown because too many files have changed in this diff. Show more