From 6d2169ecea09dfc03bd1e91197da11018881ebf6 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Fri, 9 Jan 2026 13:07:14 -0800 Subject: [PATCH 01/11] feat: remaining PFC checks encoded as workflow --- .github/workflows/code-generation-check.yml | 82 +++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 .github/workflows/code-generation-check.yml diff --git a/.github/workflows/code-generation-check.yml b/.github/workflows/code-generation-check.yml new file mode 100644 index 00000000000..43d8f8f9f90 --- /dev/null +++ b/.github/workflows/code-generation-check.yml @@ -0,0 +1,82 @@ +# Copyright 2025 Specter Ops, Inc. +# +# Licensed under the Apache License, Version 2.0 +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 + +name: Code Generation Check + +on: + workflow_dispatch: + +jobs: + code-generation-check: + runs-on: ubuntu-latest + + steps: + - name: Checkout source code for this repository + uses: actions/checkout@v4 + + - name: Install Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + cache: true + check-latest: true + + - name: Install Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Install Yarn + run: | + npm install --global yarn + + - name: Run stbernard deps + run: | + go tool stbernard deps + + - name: Run stbernard modsync + run: | + go tool stbernard modsync + + - name: Run stbernard generate + run: | + go tool stbernard generate + + - name: Check for uncommitted changes + id: show + run: | + go tool stbernard show + + - name: Fail with helpful message + if: failure() + run: | + echo "::error::Code generation check failed!" + echo "" + echo "==========================================" + echo " CODE GENERATION CHECK FAILED" + echo "==========================================" + echo "" + echo "This PR has uncommitted generated code or dependency changes." + echo "" + echo "Please run the following command locally and commit the changes:" + echo "" + echo " just prepare-for-codereview" + echo "" + echo "Then push the updated changes to this PR." + echo "" + echo "==========================================" + exit 1 + From 3b4d4ef7144e6212659aa77983a002ca8a13c44b Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Fri, 9 Jan 2026 13:18:15 -0800 Subject: [PATCH 02/11] fix: try again --- .github/workflows/code-generation-check.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/code-generation-check.yml b/.github/workflows/code-generation-check.yml index 43d8f8f9f90..d824432adee 100644 --- a/.github/workflows/code-generation-check.yml +++ b/.github/workflows/code-generation-check.yml @@ -17,7 +17,9 @@ name: Code Generation Check on: - workflow_dispatch: + push: + branches: + - BED-6802 jobs: code-generation-check: @@ -79,4 +81,3 @@ jobs: echo "" echo "==========================================" exit 1 - From d97ec93e320ac4184ee3d7446503ec3b28df4426 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Fri, 9 Jan 2026 13:29:32 -0800 Subject: [PATCH 03/11] fix: missing license check and handle no tags --- .github/workflows/code-generation-check.yml | 6 +++- packages/go/stbernard/command/show/show.go | 40 ++++++++++++++------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/.github/workflows/code-generation-check.yml b/.github/workflows/code-generation-check.yml index d824432adee..c6a648a481b 100644 --- a/.github/workflows/code-generation-check.yml +++ b/.github/workflows/code-generation-check.yml @@ -57,10 +57,14 @@ jobs: run: | go tool stbernard generate + - name: Run stbernard license + run: | + go tool stbernard license + - name: Check for uncommitted changes id: show run: | - go tool stbernard show + go tool stbernard show -no-version - name: Fail with helpful message if: failure() diff --git a/packages/go/stbernard/command/show/show.go b/packages/go/stbernard/command/show/show.go index 3c63fc48739..3c1af718988 100644 --- a/packages/go/stbernard/command/show/show.go +++ b/packages/go/stbernard/command/show/show.go @@ -34,7 +34,8 @@ const ( ) type command struct { - env environment.Environment + env environment.Environment + noVersion bool } type repository struct { @@ -65,6 +66,8 @@ func (s *command) Name() string { func (s *command) Parse(cmdIndex int) error { cmd := flag.NewFlagSet(Name, flag.ExitOnError) + cmd.BoolVar(&s.noVersion, "no-version", false, "Disable version tag checking (for environments without git tags)") + cmd.Usage = func() { w := flag.CommandLine.Output() fmt.Fprintf(w, "%s\n\nUsage: %s %s [OPTIONS]\n\nOptions:\n", Usage, filepath.Base(os.Args[0]), Name) @@ -93,7 +96,11 @@ func (s *command) Run() error { for _, repo := range repos { fmt.Printf("Repository Report For %s\n", repo.path) fmt.Printf("Current HEAD: %s\n", repo.sha) - fmt.Printf("Detected version: %s\n", repo.version) + if s.noVersion { + fmt.Println("Detected version: (skipped)") + } else { + fmt.Printf("Detected version: %s\n", repo.version) + } if !repo.clean { fmt.Println("CHANGES DETECTED - please commit outstanding changes and run the command again") return fmt.Errorf("changes detected in git repository - please commit outstanding changes and run the command again") @@ -123,18 +130,27 @@ func (s *command) submodulesCheck(paths []string) ([]repository, error) { func (s *command) repositoryCheck(cwd string) (repository, error) { var repo repository - if sha, err := git.FetchCurrentCommitSHA(cwd, s.env); err != nil { + sha, err := git.FetchCurrentCommitSHA(cwd, s.env) + if err != nil { return repo, fmt.Errorf("fetching current commit sha: %w", err) - } else if version, err := git.ParseLatestVersionFromTags(cwd, s.env); err != nil { - return repo, fmt.Errorf("parsing version: %w", err) - } else if clean, err := git.CheckClean(cwd, s.env); err != nil { - return repo, fmt.Errorf("checking repository clean: %w", err) - } else { - repo.path = cwd - repo.sha = sha + } + + repo.path = cwd + repo.sha = sha + + if !s.noVersion { + version, err := git.ParseLatestVersionFromTags(cwd, s.env) + if err != nil { + return repo, fmt.Errorf("parsing version: %w", err) + } repo.version = version - repo.clean = clean + } - return repo, nil + clean, err := git.CheckClean(cwd, s.env) + if err != nil { + return repo, fmt.Errorf("checking repository clean: %w", err) } + repo.clean = clean + + return repo, nil } From 458138a6f84cd05df576e02910aed4fa8cf105e4 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Fri, 9 Jan 2026 13:43:58 -0800 Subject: [PATCH 04/11] fix: fetch depth 2 to allow for diff-index to work --- .github/workflows/code-generation-check.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/code-generation-check.yml b/.github/workflows/code-generation-check.yml index c6a648a481b..fbd36273e37 100644 --- a/.github/workflows/code-generation-check.yml +++ b/.github/workflows/code-generation-check.yml @@ -28,6 +28,8 @@ jobs: steps: - name: Checkout source code for this repository uses: actions/checkout@v4 + with: + fetch-depth: 2 - name: Install Go uses: actions/setup-go@v5 From bcdf611a4383c3b06ae58d94dc144ed00a3a9ad7 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Fri, 9 Jan 2026 13:55:17 -0800 Subject: [PATCH 05/11] fix: better diff handling --- packages/go/stbernard/git/git.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/go/stbernard/git/git.go b/packages/go/stbernard/git/git.go index eab5d2e94e1..ac001833d85 100644 --- a/packages/go/stbernard/git/git.go +++ b/packages/go/stbernard/git/git.go @@ -95,19 +95,19 @@ func CheckClean(cwd string, env environment.Environment) (bool, error) { diffIndexPlan := cmdrunner.ExecutionPlan{ Command: "git", - Args: []string{"diff-index", "--quiet", "HEAD", "--"}, + Args: []string{"diff", "--name-only"}, Path: cwd, Env: env.Slice(), SuppressErrors: true, } result, err := cmdrunner.Run(context.TODO(), diffIndexPlan) if err != nil { - // Failure was due to dirty workspace - if errors.Is(err, cmdrunner.ErrCmdExecutionFailed) && result.ReturnCode == 1 { - return false, nil - } else { - return false, fmt.Errorf("git diff-index: %w", err) - } + return false, fmt.Errorf("git diff: %w", err) + } + + if len(result.StandardOutput.Bytes()) > 0 { + slog.Info("Repository is dirty", slog.String("status", result.StandardOutput.String())) + return false, nil } slog.Info(fmt.Sprintf("Finished checking repository clean for %s", cwd)) From b7042b4ff775dbb28c6c832f5d06c063fac15507 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Fri, 9 Jan 2026 15:05:02 -0800 Subject: [PATCH 06/11] fix: formatting --- cmd/ui/public/mockServiceWorker.js | 475 ++++++++++++++--------------- 1 file changed, 237 insertions(+), 238 deletions(-) diff --git a/cmd/ui/public/mockServiceWorker.js b/cmd/ui/public/mockServiceWorker.js index 0bf11dca53f..792884b2c3c 100644 --- a/cmd/ui/public/mockServiceWorker.js +++ b/cmd/ui/public/mockServiceWorker.js @@ -23,163 +23,162 @@ * - Please do NOT serve this file on production. */ -const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' -const activeClientIds = new Set() +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70'; +const activeClientIds = new Set(); self.addEventListener('install', function () { - self.skipWaiting() -}) + self.skipWaiting(); +}); self.addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()) -}) + event.waitUntil(self.clients.claim()); +}); self.addEventListener('message', async function (event) { - const clientId = event.source.id + const clientId = event.source.id; - if (!clientId || !self.clients) { - return - } - - const client = await self.clients.get(clientId) - - if (!client) { - return - } + if (!clientId || !self.clients) { + return; + } - const allClients = await self.clients.matchAll({ - type: 'window', - }) + const client = await self.clients.get(clientId); - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }) - break + if (!client) { + return; } - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: INTEGRITY_CHECKSUM, - }) - break + const allClients = await self.clients.matchAll({ + type: 'window', + }); + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }); + break; + } + + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }); + break; + } + + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId); + + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }); + break; + } + + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId); + break; + } + + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId); + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId; + }); + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister(); + } + + break; + } } +}); - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId) +self.addEventListener('fetch', function (event) { + const { request } = event; + const accept = request.headers.get('accept') || ''; - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: true, - }) - break + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return; } - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId) - break + // Bypass navigation requests. + if (request.mode === 'navigate') { + return; } - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId) - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId - }) - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister() - } + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return; + } - break + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return; } - } -}) -self.addEventListener('fetch', function (event) { - const { request } = event - const accept = request.headers.get('accept') || '' - - // Bypass server-sent events. - if (accept.includes('text/event-stream')) { - return - } - - // Bypass navigation requests. - if (request.mode === 'navigate') { - return - } - - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return - } - - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return - } - - // Generate unique request ID. - const requestId = Math.random().toString(16).slice(2) - - event.respondWith( - handleRequest(event, requestId).catch((error) => { - if (error.name === 'NetworkError') { - console.warn( - '[MSW] Successfully emulated a network error for the "%s %s" request.', - request.method, - request.url, - ) - return - } - - // At this point, any exception indicates an issue with the original request/response. - console.error( - `\ + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2); + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url + ); + return; + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ [MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, - request.method, - request.url, - `${error.name}: ${error.message}`, - ) - }), - ) -}) + request.method, + request.url, + `${error.name}: ${error.message}` + ); + }) + ); +}); async function handleRequest(event, requestId) { - const client = await resolveMainClient(event) - const response = await getResponse(event, client, requestId) - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - ;(async function () { - const clonedResponse = response.clone() - sendToClient(client, { - type: 'RESPONSE', - payload: { - requestId, - type: clonedResponse.type, - ok: clonedResponse.ok, - status: clonedResponse.status, - statusText: clonedResponse.statusText, - body: - clonedResponse.body === null ? null : await clonedResponse.text(), - headers: Object.fromEntries(clonedResponse.headers.entries()), - redirected: clonedResponse.redirected, - }, - }) - })() - } + const client = await resolveMainClient(event); + const response = await getResponse(event, client, requestId); + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + (async function () { + const clonedResponse = response.clone(); + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }); + })(); + } - return response + return response; } // Resolve the main client for the given event. @@ -187,132 +186,132 @@ async function handleRequest(event, requestId) { // that registered the worker. It's with the latter the worker should // communicate with during the response resolving phase. async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId) - - if (client?.frameType === 'top-level') { - return client - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }) - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible' - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id) - }) + const client = await self.clients.get(event.clientId); + + if (client?.frameType === 'top-level') { + return client; + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }); + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible'; + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id); + }); } async function getResponse(event, client, requestId) { - const { request } = event - const clonedRequest = request.clone() - - function passthrough() { - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const headers = Object.fromEntries(clonedRequest.headers.entries()) - - // Remove MSW-specific request headers so the bypassed requests - // comply with the server's CORS preflight check. - // Operate with the headers as an object because request "Headers" - // are immutable. - delete headers['x-msw-bypass'] - - return fetch(clonedRequest, { headers }) - } - - // Bypass mocking when the client is not active. - if (!client) { - return passthrough() - } - - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return passthrough() - } - - // Bypass requests with the explicit bypass header. - // Such requests can be issued by "ctx.fetch()". - if (request.headers.get('x-msw-bypass') === 'true') { - return passthrough() - } - - // Notify the client that a request has been intercepted. - const clientMessage = await sendToClient(client, { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - mode: request.mode, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: await request.text(), - bodyUsed: request.bodyUsed, - keepalive: request.keepalive, - }, - }) - - switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data) + const { request } = event; + const clonedRequest = request.clone(); + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()); + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass']; + + return fetch(clonedRequest, { headers }); } - case 'MOCK_NOT_FOUND': { - return passthrough() + // Bypass mocking when the client is not active. + if (!client) { + return passthrough(); } - case 'NETWORK_ERROR': { - const { name, message } = clientMessage.data - const networkError = new Error(message) - networkError.name = name + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough(); + } - // Rejecting a "respondWith" promise emulates a network error. - throw networkError + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough(); } - } - return passthrough() + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }); + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data); + } + + case 'MOCK_NOT_FOUND': { + return passthrough(); + } + + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data; + const networkError = new Error(message); + networkError.name = name; + + // Rejecting a "respondWith" promise emulates a network error. + throw networkError; + } + } + + return passthrough(); } function sendToClient(client, message) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel() + return new Promise((resolve, reject) => { + const channel = new MessageChannel(); - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error) - } + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error); + } - resolve(event.data) - } + resolve(event.data); + }; - client.postMessage(message, [channel.port2]) - }) + client.postMessage(message, [channel.port2]); + }); } function sleep(timeMs) { - return new Promise((resolve) => { - setTimeout(resolve, timeMs) - }) + return new Promise((resolve) => { + setTimeout(resolve, timeMs); + }); } async function respondWithMock(response) { - await sleep(response.delay) - return new Response(response.body, response) + await sleep(response.delay); + return new Response(response.body, response); } From 74039aecd5a6ce76c831cdbed292c548cbb08cba Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Fri, 9 Jan 2026 15:15:42 -0800 Subject: [PATCH 07/11] fix: show all the problems --- packages/go/stbernard/git/git.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/go/stbernard/git/git.go b/packages/go/stbernard/git/git.go index ac001833d85..355ea3e08cf 100644 --- a/packages/go/stbernard/git/git.go +++ b/packages/go/stbernard/git/git.go @@ -95,7 +95,7 @@ func CheckClean(cwd string, env environment.Environment) (bool, error) { diffIndexPlan := cmdrunner.ExecutionPlan{ Command: "git", - Args: []string{"diff", "--name-only"}, + Args: []string{"--no-pager", "diff"}, Path: cwd, Env: env.Slice(), SuppressErrors: true, @@ -106,7 +106,8 @@ func CheckClean(cwd string, env environment.Environment) (bool, error) { } if len(result.StandardOutput.Bytes()) > 0 { - slog.Info("Repository is dirty", slog.String("status", result.StandardOutput.String())) + slog.Info("Repository is dirty") + fmt.Fprint(os.Stdout, result.StandardOutput.String()) return false, nil } From ea5d7a16a50dbfb60b7567e9554a0a61aa149811 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Mon, 12 Jan 2026 10:50:37 -0800 Subject: [PATCH 08/11] chore: try to understand why things are off the rails --- .github/workflows/code-generation-check.yml | 4 ++++ packages/go/stbernard/workspace/workspace.go | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/.github/workflows/code-generation-check.yml b/.github/workflows/code-generation-check.yml index fbd36273e37..400e3a5f690 100644 --- a/.github/workflows/code-generation-check.yml +++ b/.github/workflows/code-generation-check.yml @@ -47,6 +47,10 @@ jobs: run: | npm install --global yarn + - name: Sanity Check CWD + run: | + ls + - name: Run stbernard deps run: | go tool stbernard deps diff --git a/packages/go/stbernard/workspace/workspace.go b/packages/go/stbernard/workspace/workspace.go index a5379fd0c5e..dbfbad94cbe 100644 --- a/packages/go/stbernard/workspace/workspace.go +++ b/packages/go/stbernard/workspace/workspace.go @@ -20,8 +20,10 @@ import ( "context" "errors" "fmt" + "log/slog" "os" "path/filepath" + "strings" "github.com/specterops/bloodhound/packages/go/stbernard/cmdrunner" "github.com/specterops/bloodhound/packages/go/stbernard/environment" @@ -87,6 +89,14 @@ func FindPaths(env environment.Environment) (WorkspacePaths, error) { return WorkspacePaths{}, fmt.Errorf("parsing yarn workspace: %w", err) } + slog.Info( + "Detected Workspace", + slog.String("root", cwd), + slog.String("submodules", strings.Join(subPaths, "|")), + slog.String("assets", yarnWorkspaces.AssetsDir), + slog.String("yarn_workspaces", strings.Join(yarnWorkspaces.Workspaces, "|")), + ) + return WorkspacePaths{ Root: cwd, Coverage: path, From 52bc79ad289ae4a0c35af4e6824cf6f01fda0ea2 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Mon, 12 Jan 2026 11:39:04 -0800 Subject: [PATCH 09/11] fix: remove file that regenerates only on pipelines --- .vscode/launch.json | 167 ++++++++++-------- .../stbernard/command/license/internal/cmd.go | 6 +- 2 files changed, 95 insertions(+), 78 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 07be5d1abb2..0e1533fbe99 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,81 +1,94 @@ { - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "antlr-debug", + "request": "launch", + "name": "Debug Cypher Grammar", + "input": "${file}", + "grammar": "${workspaceFolder}/packages/go/cypher/grammar/Cypher.g4", + "visualParseTree": true, + }, + { + "name": "Launch Go file", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${file}", + }, + { + "name": "Debug with Docker", + "type": "go", + "host": "localhost", + "mode": "remote", + "port": 3456, + "request": "attach", + "substitutePath": [ { - "type": "antlr-debug", - "request": "launch", - "name": "Debug Cypher Grammar", - "input": "${file}", - "grammar": "${workspaceFolder}/packages/go/cypher/grammar/Cypher.g4", - "visualParseTree": true + "from": "${workspaceFolder}", + "to": "/bloodhound", }, - { - "name": "Launch Go file", - "type": "go", - "request": "launch", - "mode": "debug", - "program": "${file}" - }, - { - "name": "Debug with Docker", - "type": "go", - "host": "localhost", - "mode": "remote", - "port": 3456, - "request": "attach", - "substitutePath": [ - { - "from": "${workspaceFolder}", - "to": "/bloodhound" - } - ] - }, - { - "name": "Debug API Locally", - "type": "go", - "request": "launch", - "mode": "debug", - "program": "github.com/specterops/bloodhound/cmd/api/src", - "cwd": "${workspaceFolder}", - "args": [ - "-configfile", - "${workspaceFolder}/local-harnesses/build.config.json" - ] - }, - { - "name": "Run Package Integration Tests", - "type": "go", - "request": "launch", - "mode": "test", - "program": "${fileDirname}", - "buildFlags": "--tags=integration,serial_integration", - "showLog": true, - "env": { - "INTEGRATION_CONFIG_PATH": "${workspaceFolder}/local-harnesses/integration.config.json" - } - }, - { - "type": "node", - "request": "launch", - "name": "Launch Program", - "skipFiles": ["/**"], - "program": "${workspaceFolder}/start", - "outFiles": ["${workspaceFolder}/**/*.js"] - }, - { - "name": "Graphify", - "type": "go", - "request": "launch", - "mode": "debug", - "program": "packages/go/graphify", - "cwd": "${workspaceFolder}", - "args": ["bh-graphify", "-path=${workspaceFolder}/cmd/api/src/test/fixtures/fixtures/v6/ingest", "-outpath=${workspaceFolder}/tmp/"], - "env": { - "SB_PG_CONNECTION":"user=bloodhound password=bloodhoundcommunityedition dbname=bloodhound host=localhost port=65432" - } - } - ] + ], + }, + { + "name": "Debug API Locally", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "github.com/specterops/bloodhound/cmd/api/src", + "cwd": "${workspaceFolder}", + "args": [ + "-configfile", + "${workspaceFolder}/local-harnesses/build.config.json", + ], + }, + { + "name": "Run Package Integration Tests", + "type": "go", + "request": "launch", + "mode": "test", + "program": "${fileDirname}", + "buildFlags": "--tags=integration,serial_integration", + "showLog": true, + "env": { + "INTEGRATION_CONFIG_PATH": "${workspaceFolder}/local-harnesses/integration.config.json", + }, + }, + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "skipFiles": ["/**"], + "program": "${workspaceFolder}/start", + "outFiles": ["${workspaceFolder}/**/*.js"], + }, + { + "name": "Debug St Bernard License", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "packages/go/stbernard", + "cwd": "${workspaceFolder}", + "args": ["license"], + }, + { + "name": "Graphify", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "packages/go/graphify", + "cwd": "${workspaceFolder}", + "args": [ + "bh-graphify", + "-path=${workspaceFolder}/cmd/api/src/test/fixtures/fixtures/v6/ingest", + "-outpath=${workspaceFolder}/tmp/", + ], + "env": { + "SB_PG_CONNECTION": "user=bloodhound password=bloodhoundcommunityedition dbname=bloodhound host=localhost port=65432", + }, + }, + ], } diff --git a/packages/go/stbernard/command/license/internal/cmd.go b/packages/go/stbernard/command/license/internal/cmd.go index 37f26d6d63d..93a54f7536c 100644 --- a/packages/go/stbernard/command/license/internal/cmd.go +++ b/packages/go/stbernard/command/license/internal/cmd.go @@ -40,6 +40,7 @@ func Run(env environment.Environment) error { "justfile", filepath.Join("cmd", "api", "src", "api", "static", "assets"), filepath.Join("cmd", "api", "src", "cmd", "testidp", "samlidp"), + filepath.Join("cmd", "ui", "public", "mockServiceWorker.js"), } disallowedExtensions = []string{".zip", ".example", ".git", ".gitignore", ".gitattributes", ".png", ".mdx", ".iml", ".g4", ".sum", ".bazel", ".bzl", ".typed", ".md", ".json", ".template", "sha256", ".pyc", ".gif", ".tiff", ".lock", ".txt", ".png", ".jpg", ".jpeg", ".ico", ".gz", ".tar", ".woff", ".woff2", ".header", ".pro", ".cert", ".crt", ".key", ".example", ".sha256", ".actrc", ".all-contributorsrc", ".editorconfig", ".conf", ".dockerignore", ".prettierrc", ".lintstagedrc", ".webp", ".bak", ".java", ".interp", ".tokens", "justfile", "pgpass", "LICENSE"} now = time.Now() @@ -118,8 +119,11 @@ func Run(env environment.Environment) error { }) // ignore directories and paths that are in the ignore list - if info.IsDir() && (slices.Contains(ignoreDir, info.Name()) || ignorePath) { + if info.IsDir() && slices.Contains(ignoreDir, info.Name()) { return filepath.SkipDir + } else if ignorePath { + // Shortcut out without skipping directory (support specific file ignores) + return nil } // ignore files that are in the ignore list From 2dc6c6a349aa794a610d800e197f2ba40013fbcb Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Mon, 12 Jan 2026 11:47:36 -0800 Subject: [PATCH 10/11] fix: ignore changes to file, rather than ignoring file for license check --- .github/workflows/code-generation-check.yml | 5 +---- packages/go/stbernard/command/license/internal/cmd.go | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/code-generation-check.yml b/.github/workflows/code-generation-check.yml index 400e3a5f690..3ea1103df95 100644 --- a/.github/workflows/code-generation-check.yml +++ b/.github/workflows/code-generation-check.yml @@ -47,10 +47,6 @@ jobs: run: | npm install --global yarn - - name: Sanity Check CWD - run: | - ls - - name: Run stbernard deps run: | go tool stbernard deps @@ -70,6 +66,7 @@ jobs: - name: Check for uncommitted changes id: show run: | + git update-index --skip-worktree cmd/ui/public/mockServiceWorker.js go tool stbernard show -no-version - name: Fail with helpful message diff --git a/packages/go/stbernard/command/license/internal/cmd.go b/packages/go/stbernard/command/license/internal/cmd.go index 93a54f7536c..e5962d65820 100644 --- a/packages/go/stbernard/command/license/internal/cmd.go +++ b/packages/go/stbernard/command/license/internal/cmd.go @@ -40,7 +40,6 @@ func Run(env environment.Environment) error { "justfile", filepath.Join("cmd", "api", "src", "api", "static", "assets"), filepath.Join("cmd", "api", "src", "cmd", "testidp", "samlidp"), - filepath.Join("cmd", "ui", "public", "mockServiceWorker.js"), } disallowedExtensions = []string{".zip", ".example", ".git", ".gitignore", ".gitattributes", ".png", ".mdx", ".iml", ".g4", ".sum", ".bazel", ".bzl", ".typed", ".md", ".json", ".template", "sha256", ".pyc", ".gif", ".tiff", ".lock", ".txt", ".png", ".jpg", ".jpeg", ".ico", ".gz", ".tar", ".woff", ".woff2", ".header", ".pro", ".cert", ".crt", ".key", ".example", ".sha256", ".actrc", ".all-contributorsrc", ".editorconfig", ".conf", ".dockerignore", ".prettierrc", ".lintstagedrc", ".webp", ".bak", ".java", ".interp", ".tokens", "justfile", "pgpass", "LICENSE"} now = time.Now() From 35cb59d5efbb5007a7a75cb3640e7705eca34824 Mon Sep 17 00:00:00 2001 From: Alyx Holms Date: Mon, 12 Jan 2026 11:53:33 -0800 Subject: [PATCH 11/11] fix: reset mockServiceWorker.js --- cmd/ui/public/mockServiceWorker.js | 475 +++++++++++++++-------------- 1 file changed, 238 insertions(+), 237 deletions(-) diff --git a/cmd/ui/public/mockServiceWorker.js b/cmd/ui/public/mockServiceWorker.js index 792884b2c3c..0bf11dca53f 100644 --- a/cmd/ui/public/mockServiceWorker.js +++ b/cmd/ui/public/mockServiceWorker.js @@ -23,162 +23,163 @@ * - Please do NOT serve this file on production. */ -const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70'; -const activeClientIds = new Set(); +const INTEGRITY_CHECKSUM = '3d6b9f06410d179a7f7404d4bf4c3c70' +const activeClientIds = new Set() self.addEventListener('install', function () { - self.skipWaiting(); -}); + self.skipWaiting() +}) self.addEventListener('activate', function (event) { - event.waitUntil(self.clients.claim()); -}); + event.waitUntil(self.clients.claim()) +}) self.addEventListener('message', async function (event) { - const clientId = event.source.id; + const clientId = event.source.id - if (!clientId || !self.clients) { - return; - } + if (!clientId || !self.clients) { + return + } + + const client = await self.clients.get(clientId) - const client = await self.clients.get(clientId); + if (!client) { + return + } - if (!client) { - return; + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + switch (event.data) { + case 'KEEPALIVE_REQUEST': { + sendToClient(client, { + type: 'KEEPALIVE_RESPONSE', + }) + break } - const allClients = await self.clients.matchAll({ - type: 'window', - }); - - switch (event.data) { - case 'KEEPALIVE_REQUEST': { - sendToClient(client, { - type: 'KEEPALIVE_RESPONSE', - }); - break; - } - - case 'INTEGRITY_CHECK_REQUEST': { - sendToClient(client, { - type: 'INTEGRITY_CHECK_RESPONSE', - payload: INTEGRITY_CHECKSUM, - }); - break; - } - - case 'MOCK_ACTIVATE': { - activeClientIds.add(clientId); - - sendToClient(client, { - type: 'MOCKING_ENABLED', - payload: true, - }); - break; - } - - case 'MOCK_DEACTIVATE': { - activeClientIds.delete(clientId); - break; - } - - case 'CLIENT_CLOSED': { - activeClientIds.delete(clientId); - - const remainingClients = allClients.filter((client) => { - return client.id !== clientId; - }); - - // Unregister itself when there are no more clients - if (remainingClients.length === 0) { - self.registration.unregister(); - } - - break; - } + case 'INTEGRITY_CHECK_REQUEST': { + sendToClient(client, { + type: 'INTEGRITY_CHECK_RESPONSE', + payload: INTEGRITY_CHECKSUM, + }) + break } -}); -self.addEventListener('fetch', function (event) { - const { request } = event; - const accept = request.headers.get('accept') || ''; + case 'MOCK_ACTIVATE': { + activeClientIds.add(clientId) - // Bypass server-sent events. - if (accept.includes('text/event-stream')) { - return; + sendToClient(client, { + type: 'MOCKING_ENABLED', + payload: true, + }) + break } - // Bypass navigation requests. - if (request.mode === 'navigate') { - return; + case 'MOCK_DEACTIVATE': { + activeClientIds.delete(clientId) + break } - // Opening the DevTools triggers the "only-if-cached" request - // that cannot be handled by the worker. Bypass such requests. - if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { - return; - } + case 'CLIENT_CLOSED': { + activeClientIds.delete(clientId) + + const remainingClients = allClients.filter((client) => { + return client.id !== clientId + }) + + // Unregister itself when there are no more clients + if (remainingClients.length === 0) { + self.registration.unregister() + } - // Bypass all requests when there are no active clients. - // Prevents the self-unregistered worked from handling requests - // after it's been deleted (still remains active until the next reload). - if (activeClientIds.size === 0) { - return; + break } + } +}) - // Generate unique request ID. - const requestId = Math.random().toString(16).slice(2); - - event.respondWith( - handleRequest(event, requestId).catch((error) => { - if (error.name === 'NetworkError') { - console.warn( - '[MSW] Successfully emulated a network error for the "%s %s" request.', - request.method, - request.url - ); - return; - } - - // At this point, any exception indicates an issue with the original request/response. - console.error( - `\ +self.addEventListener('fetch', function (event) { + const { request } = event + const accept = request.headers.get('accept') || '' + + // Bypass server-sent events. + if (accept.includes('text/event-stream')) { + return + } + + // Bypass navigation requests. + if (request.mode === 'navigate') { + return + } + + // Opening the DevTools triggers the "only-if-cached" request + // that cannot be handled by the worker. Bypass such requests. + if (request.cache === 'only-if-cached' && request.mode !== 'same-origin') { + return + } + + // Bypass all requests when there are no active clients. + // Prevents the self-unregistered worked from handling requests + // after it's been deleted (still remains active until the next reload). + if (activeClientIds.size === 0) { + return + } + + // Generate unique request ID. + const requestId = Math.random().toString(16).slice(2) + + event.respondWith( + handleRequest(event, requestId).catch((error) => { + if (error.name === 'NetworkError') { + console.warn( + '[MSW] Successfully emulated a network error for the "%s %s" request.', + request.method, + request.url, + ) + return + } + + // At this point, any exception indicates an issue with the original request/response. + console.error( + `\ [MSW] Caught an exception from the "%s %s" request (%s). This is probably not a problem with Mock Service Worker. There is likely an additional logging output above.`, - request.method, - request.url, - `${error.name}: ${error.message}` - ); - }) - ); -}); + request.method, + request.url, + `${error.name}: ${error.message}`, + ) + }), + ) +}) async function handleRequest(event, requestId) { - const client = await resolveMainClient(event); - const response = await getResponse(event, client, requestId); - - // Send back the response clone for the "response:*" life-cycle events. - // Ensure MSW is active and ready to handle the message, otherwise - // this message will pend indefinitely. - if (client && activeClientIds.has(client.id)) { - (async function () { - const clonedResponse = response.clone(); - sendToClient(client, { - type: 'RESPONSE', - payload: { - requestId, - type: clonedResponse.type, - ok: clonedResponse.ok, - status: clonedResponse.status, - statusText: clonedResponse.statusText, - body: clonedResponse.body === null ? null : await clonedResponse.text(), - headers: Object.fromEntries(clonedResponse.headers.entries()), - redirected: clonedResponse.redirected, - }, - }); - })(); - } + const client = await resolveMainClient(event) + const response = await getResponse(event, client, requestId) + + // Send back the response clone for the "response:*" life-cycle events. + // Ensure MSW is active and ready to handle the message, otherwise + // this message will pend indefinitely. + if (client && activeClientIds.has(client.id)) { + ;(async function () { + const clonedResponse = response.clone() + sendToClient(client, { + type: 'RESPONSE', + payload: { + requestId, + type: clonedResponse.type, + ok: clonedResponse.ok, + status: clonedResponse.status, + statusText: clonedResponse.statusText, + body: + clonedResponse.body === null ? null : await clonedResponse.text(), + headers: Object.fromEntries(clonedResponse.headers.entries()), + redirected: clonedResponse.redirected, + }, + }) + })() + } - return response; + return response } // Resolve the main client for the given event. @@ -186,132 +187,132 @@ async function handleRequest(event, requestId) { // that registered the worker. It's with the latter the worker should // communicate with during the response resolving phase. async function resolveMainClient(event) { - const client = await self.clients.get(event.clientId); - - if (client?.frameType === 'top-level') { - return client; - } - - const allClients = await self.clients.matchAll({ - type: 'window', - }); - - return allClients - .filter((client) => { - // Get only those clients that are currently visible. - return client.visibilityState === 'visible'; - }) - .find((client) => { - // Find the client ID that's recorded in the - // set of clients that have registered the worker. - return activeClientIds.has(client.id); - }); + const client = await self.clients.get(event.clientId) + + if (client?.frameType === 'top-level') { + return client + } + + const allClients = await self.clients.matchAll({ + type: 'window', + }) + + return allClients + .filter((client) => { + // Get only those clients that are currently visible. + return client.visibilityState === 'visible' + }) + .find((client) => { + // Find the client ID that's recorded in the + // set of clients that have registered the worker. + return activeClientIds.has(client.id) + }) } async function getResponse(event, client, requestId) { - const { request } = event; - const clonedRequest = request.clone(); - - function passthrough() { - // Clone the request because it might've been already used - // (i.e. its body has been read and sent to the client). - const headers = Object.fromEntries(clonedRequest.headers.entries()); - - // Remove MSW-specific request headers so the bypassed requests - // comply with the server's CORS preflight check. - // Operate with the headers as an object because request "Headers" - // are immutable. - delete headers['x-msw-bypass']; - - return fetch(clonedRequest, { headers }); + const { request } = event + const clonedRequest = request.clone() + + function passthrough() { + // Clone the request because it might've been already used + // (i.e. its body has been read and sent to the client). + const headers = Object.fromEntries(clonedRequest.headers.entries()) + + // Remove MSW-specific request headers so the bypassed requests + // comply with the server's CORS preflight check. + // Operate with the headers as an object because request "Headers" + // are immutable. + delete headers['x-msw-bypass'] + + return fetch(clonedRequest, { headers }) + } + + // Bypass mocking when the client is not active. + if (!client) { + return passthrough() + } + + // Bypass initial page load requests (i.e. static assets). + // The absence of the immediate/parent client in the map of the active clients + // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet + // and is not ready to handle requests. + if (!activeClientIds.has(client.id)) { + return passthrough() + } + + // Bypass requests with the explicit bypass header. + // Such requests can be issued by "ctx.fetch()". + if (request.headers.get('x-msw-bypass') === 'true') { + return passthrough() + } + + // Notify the client that a request has been intercepted. + const clientMessage = await sendToClient(client, { + type: 'REQUEST', + payload: { + id: requestId, + url: request.url, + method: request.method, + headers: Object.fromEntries(request.headers.entries()), + cache: request.cache, + mode: request.mode, + credentials: request.credentials, + destination: request.destination, + integrity: request.integrity, + redirect: request.redirect, + referrer: request.referrer, + referrerPolicy: request.referrerPolicy, + body: await request.text(), + bodyUsed: request.bodyUsed, + keepalive: request.keepalive, + }, + }) + + switch (clientMessage.type) { + case 'MOCK_RESPONSE': { + return respondWithMock(clientMessage.data) } - // Bypass mocking when the client is not active. - if (!client) { - return passthrough(); + case 'MOCK_NOT_FOUND': { + return passthrough() } - // Bypass initial page load requests (i.e. static assets). - // The absence of the immediate/parent client in the map of the active clients - // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet - // and is not ready to handle requests. - if (!activeClientIds.has(client.id)) { - return passthrough(); - } + case 'NETWORK_ERROR': { + const { name, message } = clientMessage.data + const networkError = new Error(message) + networkError.name = name - // Bypass requests with the explicit bypass header. - // Such requests can be issued by "ctx.fetch()". - if (request.headers.get('x-msw-bypass') === 'true') { - return passthrough(); + // Rejecting a "respondWith" promise emulates a network error. + throw networkError } + } - // Notify the client that a request has been intercepted. - const clientMessage = await sendToClient(client, { - type: 'REQUEST', - payload: { - id: requestId, - url: request.url, - method: request.method, - headers: Object.fromEntries(request.headers.entries()), - cache: request.cache, - mode: request.mode, - credentials: request.credentials, - destination: request.destination, - integrity: request.integrity, - redirect: request.redirect, - referrer: request.referrer, - referrerPolicy: request.referrerPolicy, - body: await request.text(), - bodyUsed: request.bodyUsed, - keepalive: request.keepalive, - }, - }); - - switch (clientMessage.type) { - case 'MOCK_RESPONSE': { - return respondWithMock(clientMessage.data); - } - - case 'MOCK_NOT_FOUND': { - return passthrough(); - } - - case 'NETWORK_ERROR': { - const { name, message } = clientMessage.data; - const networkError = new Error(message); - networkError.name = name; - - // Rejecting a "respondWith" promise emulates a network error. - throw networkError; - } - } - - return passthrough(); + return passthrough() } function sendToClient(client, message) { - return new Promise((resolve, reject) => { - const channel = new MessageChannel(); + return new Promise((resolve, reject) => { + const channel = new MessageChannel() - channel.port1.onmessage = (event) => { - if (event.data && event.data.error) { - return reject(event.data.error); - } + channel.port1.onmessage = (event) => { + if (event.data && event.data.error) { + return reject(event.data.error) + } - resolve(event.data); - }; + resolve(event.data) + } - client.postMessage(message, [channel.port2]); - }); + client.postMessage(message, [channel.port2]) + }) } function sleep(timeMs) { - return new Promise((resolve) => { - setTimeout(resolve, timeMs); - }); + return new Promise((resolve) => { + setTimeout(resolve, timeMs) + }) } async function respondWithMock(response) { - await sleep(response.delay); - return new Response(response.body, response); + await sleep(response.delay) + return new Response(response.body, response) }