Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
3fdac25
feat(security): Dependencies tab, ranked by what runs in production
Makisuo Nov 1, 2026
0a1e141
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
7e6481f
fix(security): every finding view carries its production services
Makisuo Nov 1, 2026
edcaee3
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
9aaafa8
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
894cbd7
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
349710e
fix(web): Dependencies tab copy and pending production scans
Makisuo Nov 1, 2026
67739d7
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
56c6484
perf(security): one running-commits warehouse read per page, not two
Makisuo Nov 1, 2026
2a913f7
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
a86e229
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
2384bec
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
ad92a60
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
92a1f9d
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
71e9c93
fix(security): production scans are queued once per page; failed scan…
Makisuo Nov 1, 2026
c30d9d2
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
aa4f0ea
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
962b30d
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
5430392
chore(security): restore the cause summary import after the merge
Makisuo Nov 1, 2026
edd323e
Merge branch 'feat/depscan-4-scanner' into feat/depscan-5-dependencie…
Makisuo Nov 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions apps/api/src/routes/internal/dependency-scan.http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ export const HttpDependencyScanLive = HttpApiBuilder.group(MapleInternalApi, "de

return handlers
.handle("listRepositories", () =>
Effect.flatMap(CurrentTenant.Context, (tenant) => scans.listRepositories(tenant.orgId)),
Effect.flatMap(CurrentTenant.Context, (tenant) => scans.listRepositories(tenant)),
)
.handle("scanRepository", ({ params }) =>
Effect.flatMap(CurrentTenant.Context, (tenant) =>
scans.requestScan(tenant.orgId, params.repositoryId),
),
)
.handle("listFindings", ({ query }) =>
Effect.flatMap(CurrentTenant.Context, (tenant) => scans.listFindings(tenant.orgId, query)),
Effect.flatMap(CurrentTenant.Context, (tenant) => scans.listFindings(tenant, query)),
)
.handle("updateFinding", ({ params, payload }) =>
Effect.gen(function* () {
Expand Down
45 changes: 42 additions & 3 deletions apps/web/src/lab/entries/security.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { Clock, Effect, Option, Schema } from "effect"
import { Command } from "foldkit"
import type { Html, HtmlBuilder } from "foldkit/html"
import { defineMessageUnion } from "foldkit/message"
import * as DependenciesPage from "../../page/security/dependencies/model"
import * as DependenciesUpdate from "../../page/security/dependencies/update"
import { view as dependenciesView } from "../../page/security/dependencies/view"
import * as FindingPage from "../../page/security/finding/model"
import * as FindingUpdate from "../../page/security/finding/update"
import { breadcrumbsOf as findingCrumbs, view as findingView } from "../../page/security/finding/view"
Expand All @@ -27,6 +30,7 @@ const Model = Schema.Struct({
run: Schema.NullOr(RunPage.Model),
finding: Schema.NullOr(FindingPage.Model),
telemetry: Schema.NullOr(TelemetryPage.Model),
dependencies: Schema.NullOr(DependenciesPage.Model),
})
type Model = typeof Model.Type

Expand All @@ -35,6 +39,7 @@ const Message = defineMessageUnion({
GotRunMessage: { message: RunPage.Message },
GotFindingMessage: { message: FindingPage.Message },
GotTelemetryMessage: { message: TelemetryPage.Message },
GotDependenciesMessage: { message: DependenciesPage.Message },
})
type Message = typeof Message.Type

Expand All @@ -54,7 +59,14 @@ const flags = Effect.all({

const init = (input: typeof Flags.Type) => {
const built = build(input.scenario, input.nowMs)
const empty: Model = { scenario: input.scenario, tabs: null, run: null, finding: null, telemetry: null }
const empty: Model = {
scenario: input.scenario,
tabs: null,
run: null,
finding: null,
telemetry: null,
dependencies: null,
}
switch (built._tag) {
case "Tabs":
return {
Expand Down Expand Up @@ -84,6 +96,13 @@ const init = (input: typeof Flags.Type) => {
Message.GotTelemetryMessage({ message }),
),
}
case "Dependencies":
return {
model: { ...empty, dependencies: built.step.model },
commands: Command.mapMessages(built.step.commands, (message) =>
Message.GotDependenciesMessage({ message }),
),
}
}
}

Expand Down Expand Up @@ -129,6 +148,16 @@ const update = (model: Model, message: Message) =>
),
}
},
GotDependenciesMessage: ({ message: inner }) => {
if (model.dependencies === null) return { model }
const next = DependenciesUpdate.update(model.dependencies, inner, context)
return {
model: { ...model, dependencies: next.model },
commands: Command.mapMessages((next.commands ?? []).filter(isLocalCommand), (child) =>
Message.GotDependenciesMessage({ message: child }),
),
}
},
})

const view = (model: Model, h: HtmlBuilder<Message>): Html =>
Expand Down Expand Up @@ -164,7 +193,15 @@ const view = (model: Model, h: HtmlBuilder<Message>): Html =>
viewInputs: { context },
toParentMessage: (message) => Message.GotTelemetryMessage({ message }),
})
: h.empty
: model.dependencies !== null
? h.submodel({
slotId: "security-lab-dependencies",
model: model.dependencies,
view: dependenciesView,
viewInputs: { context },
toParentMessage: (message) => Message.GotDependenciesMessage({ message }),
})
: h.empty

export const lab = defineShellLab("Security Center", {
Model,
Expand All @@ -184,5 +221,7 @@ export const lab = defineShellLab("Security Center", {
? findingCrumbs(model.finding)
: model.telemetry !== null
? tabsCrumbs(model.telemetry.tab)
: [],
: model.dependencies !== null
? tabsCrumbs("dependencies")
: [],
})
Loading
Loading