From 2bf274849c89c6e3e1577b46bc73cb7a54802b3e Mon Sep 17 00:00:00 2001 From: ahenao Date: Tue, 4 Nov 2025 14:20:02 -0500 Subject: [PATCH 1/4] fixed state of training selection when moving to different tabs within caldera --- gui/views/training.vue | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/gui/views/training.vue b/gui/views/training.vue index 83480fd..cc680ec 100644 --- a/gui/views/training.vue +++ b/gui/views/training.vue @@ -25,20 +25,48 @@ onBeforeUnmount(() => { }); onMounted(async () => { + // ✅ Restore previous state before loading data + const savedState = localStorage.getItem("trainingState"); + if (savedState) { + try { + const parsed = JSON.parse(savedState); + selectedCert.value = parsed.selectedCert || ""; + selectedBadge.value = parsed.selectedBadge || ""; + } catch (err) { + console.warn("Failed to parse saved training state:", err); + } + } + + // ✅ Existing functionality (keep this intact) const res = await $api.get("/plugin/training/certs"); certificates.value = res.data.certificates; + let confettiScript = document.createElement("script"); confettiScript.setAttribute( "src", "https://cdn.jsdelivr.net/npm/canvas-confetti@1.9.2/dist/confetti.browser.min.js" ); document.head.appendChild(confettiScript); + if (updateInterval) clearInterval(updateInterval); updateInterval = setInterval(async () => { getTraining(); - }, "3000"); + }, 3000); // <-- you can also remove the quotes, should be a number not a string }); +// ✅ New: persist user selections when they change +watch( + [selectedCert, selectedBadge], + () => { + const state = { + selectedCert: selectedCert.value, + selectedBadge: selectedBadge.value, + }; + localStorage.setItem("trainingState", JSON.stringify(state)); + }, + { deep: true } +); + watch(selectedCert, (newValue) => { getTraining(); }); From 2ddd4f4a26b87f6a7680b9b6e7d4cc4daf16987f Mon Sep 17 00:00:00 2001 From: ahenao Date: Fri, 7 Nov 2025 13:58:37 -0500 Subject: [PATCH 2/4] Fix: make flags independent of previous completion, NOT changing each individual flag --- app/training_api.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/training_api.py b/app/training_api.py index d2fdd79..d1f5d97 100644 --- a/app/training_api.py +++ b/app/training_api.py @@ -75,8 +75,10 @@ async def retrieve_flags(self, request): flag.completed = flag.verify(answer) else: flag.completed = await flag.verify(self.services) - if not hasattr(cert, 'cert_type'): - break +# Process all flags independently — don't stop after first incomplete one +# if not hasattr(cert, 'cert_type'): +# break + except Exception as e: logging.error(e) return web.json_response(dict(badges=[b.display for b in badges])) From c942b302de9b44654b719aef26a2518e629c8323 Mon Sep 17 00:00:00 2001 From: ahenao Date: Tue, 11 Nov 2025 12:23:28 -0500 Subject: [PATCH 3/4] Make any combination of manual or potential links result in successful operation --- app/flags/operations/flag_3.py | 40 ++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/app/flags/operations/flag_3.py b/app/flags/operations/flag_3.py index e7a213c..48beeca 100644 --- a/app/flags/operations/flag_3.py +++ b/app/flags/operations/flag_3.py @@ -1,8 +1,8 @@ +import logging from plugins.training.app.c_flag import Flag - class OperationsFlag3(Flag): - name = 'Empty operation' + name = 'Empty Op' challenge = ( 'Run and finish an operation without selecting any agent groups or adversary profiles. Add at least 5 manual ' @@ -10,12 +10,38 @@ class OperationsFlag3(Flag): ) extra_info = ( - 'During an autonomous adversary emulation exercise, the operation will only run tasks in the adversary profile. ' - 'Manual and potential links allow an operator to "toss in" additional TTPs into a live, autonomous operation.' + 'During an autonomous adversary emulation exercise, the operation will only run tasks in the adversary profile. ' + 'Manual and potential links allow an operator to "toss in" additional TTPs into a live, autonomous operation.' ) async def verify(self, services): - for op in await services.get('data_svc').locate('operations'): - if op.finish and op.adversary.adversary_id == 'ad-hoc' and len(op.chain) >= 5 and not op.group: + data_svc = services.get('data_svc') + if not data_svc: + logging.error("[training.flag3] data_svc is None!") + return False + + operations = await data_svc.locate('operations') + for op in operations: + logging.info(f"[training.flag3] Checking operation '{op.name}' | state={op.state}, group={op.group}, adversary_id={getattr(op.adversary, 'adversary_id', None)}") + + # Gather all link lists that might exist + chain_links = getattr(op, 'chain', []) or [] + potential_links = getattr(op, 'potential_links', []) or [] + manual_links = getattr(op, 'links', []) or [] + + total_links = len(chain_links) + len(potential_links) + len(manual_links) + + logging.info(f"[training.flag3] Found {len(chain_links)} chain links, {len(potential_links)} potential links, {len(manual_links)} manual links (total={total_links})") + + # Verify completion conditions + if ( + op.finish + and getattr(op.adversary, 'adversary_id', None) == 'ad-hoc' + and total_links >= 5 + and not op.group + ): + logging.info(f"[training.flag3] ✅ Operation '{op.name}' meets all criteria!") return True - return False + + logging.info("[training.flag3] ❌ No operation met the criteria.") + return False \ No newline at end of file From 4c76a2a71cdc2b4dd63575efac3eef25bb720b4e Mon Sep 17 00:00:00 2001 From: ahenao Date: Tue, 11 Nov 2025 12:41:02 -0500 Subject: [PATCH 4/4] Fix language in solution guides for clarity and matching Caldera UI --- solution_guides/AdvancedFlag0.md | 2 +- solution_guides/AdvancedFlag1.md | 6 +++--- solution_guides/OperationsFlag0.md | 3 +-- solution_guides/OperationsFlag1.md | 3 +-- solution_guides/OperationsFlag2.md | 5 ++--- solution_guides/OperationsFlag3.md | 3 +-- 6 files changed, 9 insertions(+), 13 deletions(-) diff --git a/solution_guides/AdvancedFlag0.md b/solution_guides/AdvancedFlag0.md index 0c656c7..c71e5a3 100644 --- a/solution_guides/AdvancedFlag0.md +++ b/solution_guides/AdvancedFlag0.md @@ -1,4 +1,4 @@ -1. Select `CONFIGURATION > configuration`. +1. Select `CONFIGURATION > settings`. 1. Change the `app.contact.http` value to a URL with an externally-facing IP address (not http://127.0.0.1:8888) 1. Press the `Update` button in the `app.contact.http` row. 1. Task completed. diff --git a/solution_guides/AdvancedFlag1.md b/solution_guides/AdvancedFlag1.md index b811076..30a6e71 100644 --- a/solution_guides/AdvancedFlag1.md +++ b/solution_guides/AdvancedFlag1.md @@ -1,11 +1,11 @@ 1. Select `CONFIGURATION > fact sources`. -1. Press the `+ Create Source` button to open the source creation menu. +1. Press the `+ New Source` button to open the source creation menu. 1. Replace `New source` in the textbox with `better basic`. -1. Under `Facts`, press the `+ new fact` button. +1. Under `Facts`, press the `+ add fact` button. 1. Enter a `Fact Trait` (ex: "host.file.path"). 1. Enter a `Value` (ex: "C:\Windows\System32\calc.exe"). 1. Press the `Save` button. -1. Under `Rules`, press the `+ new rule` button. +1. Under `Rules`, press the `+ add rule` button. 1. Enter a `Fact Trait` (ex: "host.file.path"). 1. Next to `ALLOW` enter a `Match` (ex: "C:\Windows\System32\calc.exe"). 1. Press the `Save` button. diff --git a/solution_guides/OperationsFlag0.md b/solution_guides/OperationsFlag0.md index aeb97a3..b315b6f 100644 --- a/solution_guides/OperationsFlag0.md +++ b/solution_guides/OperationsFlag0.md @@ -1,10 +1,9 @@ 1. First, ensure that an agent is deployed and is responsive. 1. Select `CAMPAIGNS > operations`. -1. Click the `+ Create Operation` button to open the `Start New Operation` menu. +1. Click the `+ New Operation` button to open the `Start New Operation` menu. 1. Give the operation a name. 1. Select the `Check` adversary from the `Adversary` dropdown. 1. Select `basic` from the `Fact source` menu. -1. Press `ADVANCED` to open the advanced options dialog. 1. Select `Auto close operation` from the `Auto-close`radio group. 1. Press `Start` to run the operation. 1. Wait for the operation to complete. diff --git a/solution_guides/OperationsFlag1.md b/solution_guides/OperationsFlag1.md index 83398ca..4ffd3e3 100644 --- a/solution_guides/OperationsFlag1.md +++ b/solution_guides/OperationsFlag1.md @@ -1,10 +1,9 @@ 1. First, ensure that an agent is deployed and is responsive. 1. Select `CAMPAIGNS > operations`. -1. Click the `+ Create Operation` button to open the `Start New Operation` menu. +1. Click the `+ New Operation` button to open the `Start New Operation` menu. 1. Give the operation a name. 1. Select the `Check` adversary from the `Adversary` dropdown. 1. Select `basic` from the `Fact source` menu. -1. Press `ADVANCED` to open the advanced options dialog. 1. In the `Obfuscators` button group, choose `base64`. 1. Select `Auto close operation` from the `Auto-close`radio group. 1. In the `Jitter (sec/sec)` select `10/20`. diff --git a/solution_guides/OperationsFlag2.md b/solution_guides/OperationsFlag2.md index 974e100..1fea3bb 100644 --- a/solution_guides/OperationsFlag2.md +++ b/solution_guides/OperationsFlag2.md @@ -1,13 +1,12 @@ 1. First, ensure that an agent is deployed and is responsive. 1. Select `CAMPAIGNS > operations`. -1. Click the `+ Create Operation` button to open the `Start New Operation` menu. +1. Click the `+ New Operation` button to open the `Start New Operation` menu. 1. Give the operation a name. 1. Select the `Discovery` adversary from the `Adversary` dropdown. 1. Select `basic` from the `Fact source` menu. -1. Press `ADVANCED` to open the advanced options dialog. 1. Select `Require manual approval` in the `Autonomous` radio group. 1. Press `Start` to run the operation. -1. In the list of commands a `Review Command` button will appear with each new command. Press `Review Command` button and confirm by clicking `Approve` in the dialog box that appears. +1. In the list of commands a `View Command` button will appear with each new command. Press `Review Command` button and confirm by clicking `Approve` in the dialog box that appears. Repeat for each command. 1. Press `Stop` button to finish the operation. 1. Wait for the operation to complete. 1. Task completed. \ No newline at end of file diff --git a/solution_guides/OperationsFlag3.md b/solution_guides/OperationsFlag3.md index 4bfb700..6d1bb29 100644 --- a/solution_guides/OperationsFlag3.md +++ b/solution_guides/OperationsFlag3.md @@ -1,10 +1,9 @@ 1. First, ensure that an agent is deployed and is responsive. 1. Select `CAMPAIGNS > operations`. -1. Click the `+ Create Operation` button to open the `Start New Operation` menu. +1. Click the `+ New Operation` button to open the `Start New Operation` menu. 1. Give the operation a name. 1. Ensure that `No adversary (manual)` is selected from the `Adversary` dropdown. 1. Select `basic` from the `Fact source` menu. -1. Press `ADVANCED` to open the advanced options dialog. 1. Select `Require manual approval` in the `Autonomous` radio group. 1. In the `Run state`, select `Pause on start` option. 1. Press `Start` to run the operation.