@lievclouds/dsh-cortex 0.3.4 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client.js +92 -92
- package/index.js +9 -7
- package/package.json +1 -1
- package/src/cortex-ui/dsh/css-overrides.css +8 -0
- package/src/cortex-ui/renderer/components/asset/DeleteAssetDialog.tsx +3 -2
- package/src/cortex-ui/renderer/components/poc/PocConfigPanel.tsx +3 -2
- package/src/cortex-ui/renderer/components/retest/RetestProxyDialog.tsx +3 -2
- package/src/cortex-ui/renderer/components/snapshot/DeleteSnapshotDialog.tsx +4 -2
- package/src/cortex-ui/renderer/components/vuln/BulkDeleteDialog.tsx +3 -2
- package/src/cortex-ui/renderer/components/vuln/ConfirmDeleteDialog.tsx +3 -2
- package/src/host/cortex/index.js +14 -9
- package/src/host/http.js +8 -1
package/index.js
CHANGED
|
@@ -41,14 +41,16 @@ export function apply(ctx, config) {
|
|
|
41
41
|
name: 'dsh-cortex.tools',
|
|
42
42
|
order: 500,
|
|
43
43
|
text: [
|
|
44
|
-
'## Cortex (dsh-cortex plugin,
|
|
45
|
-
'
|
|
46
|
-
'-
|
|
44
|
+
'## Cortex (dsh-cortex plugin, always active)',
|
|
45
|
+
'This plugin is installed and injects this guide automatically: the cortex_* tools below are available in every conversation without a skill or an MCP server.',
|
|
46
|
+
'When the user asks for ANY authorized penetration-testing work (渗透测试 / SRC / 漏洞挖掘 / 安全评估 / 资产与接口侦察 / 内网横向 / 漏洞复测), use the Cortex tools proactively instead of relying on memory:',
|
|
47
|
+
'- Reuse memory first: `cortex_list_snapshots` + `cortex_get_snapshot` for task progress, `cortex_list_vulns` + `cortex_get_asset` for existing findings/assets; `cortex_list_assets` before repeating recon.',
|
|
48
|
+
'- Persist confirmed findings: dedupe with `cortex_list_vulns`, then `cortex_save_vuln` (reproduction_steps in Markdown).',
|
|
49
|
+
'- Persist recon context: `cortex_sync_asset_info` (mode=merge) for fingerprints, interfaces (raw HTTP), leaks and test parameters.',
|
|
47
50
|
'- Status / retest: `cortex_update_vuln_status`, `cortex_append_retest` (append-only, never overwrite original evidence).',
|
|
48
|
-
'-
|
|
49
|
-
'
|
|
50
|
-
'Examples
|
|
51
|
-
'If a tools call fails, report the failure; never claim a record was stored without a successful result.',
|
|
51
|
+
'- Summaries: `cortex_internal_overview`, `cortex_graph_summary`.',
|
|
52
|
+
'Do not skip these calls merely because no skill was loaded. If a tool call fails, report the failure; never claim a record was saved without a successful result.',
|
|
53
|
+
'Examples: “列出 Cortex 里的高危漏洞”, “把刚才确认的漏洞写入 Cortex”, “把 127.0.0.1:8080 的接口同步到资产库”, “把 VULN-… 标记为已确认并追加复测结论”.',
|
|
52
54
|
].join('\n'),
|
|
53
55
|
}),
|
|
54
56
|
'dsh-cortex: usage prompt',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lievclouds/dsh-cortex",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "Cortex workbench for DeepSeek Harness: the original Cortex UI (vulnerabilities, assets, internal operations, Memory Nebula, snapshots, retest) as a native DSH panel, plus 12 native agent tools so DSH conversations use the local Cortex SQLite without MCP.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -99,6 +99,14 @@
|
|
|
99
99
|
border: 3px solid transparent;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
|
+
/* DSH draws its native window buttons above the page in the top-right corner.
|
|
103
|
+
Right-hand inspector sheets start at the very top edge, so their header
|
|
104
|
+
actions (export / close) ended up underneath those buttons. Move every sheet
|
|
105
|
+
below that band; the overlay still dims the area behind it. */
|
|
106
|
+
.dshc-cortex-root .sheet-content {
|
|
107
|
+
top: calc(0.875rem + 2.25rem);
|
|
108
|
+
}
|
|
109
|
+
|
|
102
110
|
/* ------------------------------------------------------------------------- */
|
|
103
111
|
/* DSH shell adaptations */
|
|
104
112
|
/* ------------------------------------------------------------------------- */
|
|
@@ -2,6 +2,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog'
|
|
|
2
2
|
import { Loader2, Trash2 } from 'lucide-react'
|
|
3
3
|
import { Button } from '@renderer/components/ui/button'
|
|
4
4
|
import { useI18n } from '@renderer/i18n'
|
|
5
|
+
import { getPortalContainer } from '../../../dsh/portal'
|
|
5
6
|
|
|
6
7
|
interface DeleteAssetDialogProps {
|
|
7
8
|
open: boolean
|
|
@@ -27,8 +28,8 @@ export function DeleteAssetDialog({
|
|
|
27
28
|
const { t } = useI18n()
|
|
28
29
|
|
|
29
30
|
return (
|
|
30
|
-
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
31
|
-
<DialogPrimitive.Portal>
|
|
31
|
+
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
|
|
32
|
+
<DialogPrimitive.Portal container={getPortalContainer()}>
|
|
32
33
|
<DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
|
|
33
34
|
<DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(420px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
|
|
34
35
|
<DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
|
|
@@ -37,6 +37,7 @@ import { usePocErrorMessage } from '@renderer/components/poc/usePocErrorMessage'
|
|
|
37
37
|
import { getApi } from '@renderer/lib/api'
|
|
38
38
|
import { useI18n } from '@renderer/i18n'
|
|
39
39
|
import { cn } from '@renderer/lib/utils'
|
|
40
|
+
import { getPortalContainer } from '../../../dsh/portal'
|
|
40
41
|
|
|
41
42
|
interface PocConfigPanelProps {
|
|
42
43
|
open: boolean
|
|
@@ -122,8 +123,8 @@ export function PocConfigPanel({
|
|
|
122
123
|
: null
|
|
123
124
|
|
|
124
125
|
return (
|
|
125
|
-
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
126
|
-
<DialogPrimitive.Portal>
|
|
126
|
+
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
|
|
127
|
+
<DialogPrimitive.Portal container={getPortalContainer()}>
|
|
127
128
|
<DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[85] bg-[hsl(224_18%_10%/0.3)]" />
|
|
128
129
|
<DialogPrimitive.Content
|
|
129
130
|
data-testid="retest-config-panel"
|
|
@@ -5,6 +5,7 @@ import type { PocConfigView } from '@shared/poc'
|
|
|
5
5
|
import { ProxySection } from '@renderer/components/poc/PocConfigPanel'
|
|
6
6
|
import { useI18n } from '@renderer/i18n'
|
|
7
7
|
import { getApi } from '@renderer/lib/api'
|
|
8
|
+
import { getPortalContainer } from '../../../dsh/portal'
|
|
8
9
|
|
|
9
10
|
interface RetestProxyDialogProps {
|
|
10
11
|
open: boolean
|
|
@@ -43,8 +44,8 @@ export function RetestProxyDialog({ open, onOpenChange }: RetestProxyDialogProps
|
|
|
43
44
|
}, [open, t])
|
|
44
45
|
|
|
45
46
|
return (
|
|
46
|
-
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
47
|
-
<DialogPrimitive.Portal>
|
|
47
|
+
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
|
|
48
|
+
<DialogPrimitive.Portal container={getPortalContainer()}>
|
|
48
49
|
<DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[85] bg-[hsl(224_18%_10%/0.3)]" />
|
|
49
50
|
<DialogPrimitive.Content className="sheet-content fixed left-1/2 top-1/2 z-[90] flex max-h-[88vh] w-[calc(100vw-32px)] max-w-[560px] -translate-x-1/2 -translate-y-1/2 flex-col overflow-hidden rounded-xl border border-border bg-background">
|
|
50
51
|
<div className="flex shrink-0 items-start justify-between gap-3 border-b border-border px-5 py-4">
|
|
@@ -6,6 +6,7 @@ import type { SnapshotSession } from '@shared/types'
|
|
|
6
6
|
import { Button } from '@renderer/components/ui/button'
|
|
7
7
|
import { apiUnavailableMessage, getApi, toErrorMessage } from '@renderer/lib/api'
|
|
8
8
|
import { useI18n } from '@renderer/i18n'
|
|
9
|
+
import { getPortalContainer } from '../../../dsh/portal'
|
|
9
10
|
|
|
10
11
|
interface DeleteSnapshotDialogProps {
|
|
11
12
|
open: boolean
|
|
@@ -56,13 +57,14 @@ export function DeleteSnapshotDialog({
|
|
|
56
57
|
}
|
|
57
58
|
|
|
58
59
|
return (
|
|
59
|
-
<DialogPrimitive.Root
|
|
60
|
+
<DialogPrimitive.Root
|
|
61
|
+
modal={false}
|
|
60
62
|
open={open}
|
|
61
63
|
onOpenChange={(next) => {
|
|
62
64
|
if (!pending) onOpenChange(next)
|
|
63
65
|
}}
|
|
64
66
|
>
|
|
65
|
-
<DialogPrimitive.Portal>
|
|
67
|
+
<DialogPrimitive.Portal container={getPortalContainer()}>
|
|
66
68
|
<DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
|
|
67
69
|
<DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(440px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
|
|
68
70
|
<DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
|
|
@@ -2,6 +2,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog'
|
|
|
2
2
|
import { Loader2, Trash2 } from 'lucide-react'
|
|
3
3
|
import { Button } from '@renderer/components/ui/button'
|
|
4
4
|
import { useI18n } from '@renderer/i18n'
|
|
5
|
+
import { getPortalContainer } from '../../../dsh/portal'
|
|
5
6
|
|
|
6
7
|
interface BulkDeleteDialogProps {
|
|
7
8
|
open: boolean
|
|
@@ -22,8 +23,8 @@ export function BulkDeleteDialog({
|
|
|
22
23
|
const { t } = useI18n()
|
|
23
24
|
|
|
24
25
|
return (
|
|
25
|
-
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
26
|
-
<DialogPrimitive.Portal>
|
|
26
|
+
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
|
|
27
|
+
<DialogPrimitive.Portal container={getPortalContainer()}>
|
|
27
28
|
<DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
|
|
28
29
|
<DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(420px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
|
|
29
30
|
<DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
|
|
@@ -2,6 +2,7 @@ import * as DialogPrimitive from '@radix-ui/react-dialog'
|
|
|
2
2
|
import { Loader2, Trash2 } from 'lucide-react'
|
|
3
3
|
import { Button } from '@renderer/components/ui/button'
|
|
4
4
|
import { useI18n } from '@renderer/i18n'
|
|
5
|
+
import { getPortalContainer } from '../../../dsh/portal'
|
|
5
6
|
|
|
6
7
|
interface ConfirmDeleteDialogProps {
|
|
7
8
|
open: boolean
|
|
@@ -22,8 +23,8 @@ export function ConfirmDeleteDialog({
|
|
|
22
23
|
const { t } = useI18n()
|
|
23
24
|
|
|
24
25
|
return (
|
|
25
|
-
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange}>
|
|
26
|
-
<DialogPrimitive.Portal>
|
|
26
|
+
<DialogPrimitive.Root open={open} onOpenChange={onOpenChange} modal={false}>
|
|
27
|
+
<DialogPrimitive.Portal container={getPortalContainer()}>
|
|
27
28
|
<DialogPrimitive.Overlay className="sheet-overlay fixed inset-0 z-[70] bg-[hsl(224_18%_10%/0.3)]" />
|
|
28
29
|
<DialogPrimitive.Content className="fixed left-1/2 top-1/2 z-[70] w-[min(420px,calc(100vw-2rem))] -translate-x-1/2 -translate-y-1/2 rounded-xl border border-border bg-background p-5 shadow-panel focus:outline-none focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ring">
|
|
29
30
|
<DialogPrimitive.Title className="text-[16px] font-medium tracking-tight text-foreground">
|
package/src/host/cortex/index.js
CHANGED
|
@@ -85,6 +85,9 @@ const ROUTES = [
|
|
|
85
85
|
|
|
86
86
|
{ pattern: /^\/snapshots$/, methods: { GET: snapshots.handleList } },
|
|
87
87
|
{ pattern: /^\/snapshots\/([^/]+)\/archive$/, methods: { POST: snapshots.handleArchive } },
|
|
88
|
+
// Both spellings exist: the vendored renderer's original Web API uses
|
|
89
|
+
// `/restore`, while the desktop contract names it `restore-previous`.
|
|
90
|
+
{ pattern: /^\/snapshots\/([^/]+)\/restore$/, methods: { POST: snapshots.handleRestorePrevious } },
|
|
88
91
|
{
|
|
89
92
|
pattern: /^\/snapshots\/([^/]+)\/restore-previous$/,
|
|
90
93
|
methods: { POST: snapshots.handleRestorePrevious },
|
|
@@ -132,19 +135,21 @@ function assertWriteFence(req, method) {
|
|
|
132
135
|
}
|
|
133
136
|
const headers = (req && req.headers) || {}
|
|
134
137
|
const origin = headers.origin
|
|
138
|
+
const fetchSite = String(headers['sec-fetch-site'] || '').trim().toLowerCase()
|
|
135
139
|
if (origin !== undefined && origin !== null && String(origin).trim().length > 0) {
|
|
136
140
|
if (!isLoopbackOrigin(String(origin))) {
|
|
137
|
-
|
|
141
|
+
// DSH renders the plugin from the custom `dsh-app://app` origin; its
|
|
142
|
+
// same-origin DELETE carries `Sec-Fetch-Site: same-origin`, which is the
|
|
143
|
+
// signal we trust (a cross-site page would report cross-site).
|
|
144
|
+
if (fetchSite !== 'same-origin' && fetchSite !== 'none') {
|
|
145
|
+
throw forbidden('FORBIDDEN_ORIGIN', 'write request origin is not allowed')
|
|
146
|
+
}
|
|
138
147
|
}
|
|
139
148
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
throw forbidden('FORBIDDEN_ORIGIN', 'cross-site write requests are not allowed', {
|
|
145
|
-
header: 'Sec-Fetch-Site',
|
|
146
|
-
})
|
|
147
|
-
}
|
|
149
|
+
if (fetchSite.length > 0 && fetchSite !== 'same-origin' && fetchSite !== 'none') {
|
|
150
|
+
throw forbidden('FORBIDDEN_ORIGIN', 'cross-site write requests are not allowed', {
|
|
151
|
+
header: 'Sec-Fetch-Site',
|
|
152
|
+
})
|
|
148
153
|
}
|
|
149
154
|
}
|
|
150
155
|
|
package/src/host/http.js
CHANGED
|
@@ -135,7 +135,14 @@ export function assertSameOriginWrite(req) {
|
|
|
135
135
|
const origin = headers.origin
|
|
136
136
|
if (origin !== undefined && origin !== null && String(origin).trim().length > 0) {
|
|
137
137
|
if (!isLoopbackOrigin(String(origin))) {
|
|
138
|
-
|
|
138
|
+
// The DSH page origin is the custom `dsh-app://app` scheme, not a
|
|
139
|
+
// loopback http(s) origin. Its same-origin fetch still carries
|
|
140
|
+
// `Sec-Fetch-Site: same-origin`; trust that rather than the scheme, so
|
|
141
|
+
// UI writes are not rejected while cross-site pages stay blocked.
|
|
142
|
+
const site = String(headers['sec-fetch-site'] || '').trim().toLowerCase()
|
|
143
|
+
if (site !== 'same-origin' && site !== 'none') {
|
|
144
|
+
throw forbidden('FORBIDDEN_ORIGIN', 'write request origin is not allowed')
|
|
145
|
+
}
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
|