@lievclouds/dsh-cortex 0.4.4 → 1.0.0

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.

Potentially problematic release.


This version of @lievclouds/dsh-cortex might be problematic. Click here for more details.

Files changed (58) hide show
  1. package/README.md +1 -1
  2. package/client.js +428 -406
  3. package/index.js +7 -0
  4. package/locale/en.json +6 -6
  5. package/locale/zh.json +6 -6
  6. package/package.json +115 -106
  7. package/src/cortex-ui/dsh/api.ts +5 -2
  8. package/src/cortex-ui/dsh/css-overrides.css +8 -0
  9. package/src/cortex-ui/renderer/App.tsx +39 -0
  10. package/src/cortex-ui/renderer/components/asset/AssetDetail.tsx +9 -1
  11. package/src/cortex-ui/renderer/components/miniapp/FileTree.tsx +161 -0
  12. package/src/cortex-ui/renderer/components/miniapp/MiniappCard.tsx +263 -0
  13. package/src/cortex-ui/renderer/components/miniapp/MiniappDetail.tsx +1493 -0
  14. package/src/cortex-ui/renderer/components/retest/CurlImportDialog.tsx +158 -0
  15. package/src/cortex-ui/renderer/components/retest/RepeaterCollectionsDialog.tsx +327 -0
  16. package/src/cortex-ui/renderer/components/retest/RepeaterHistoryDialog.tsx +242 -0
  17. package/src/cortex-ui/renderer/components/retest/RepeaterPanel.tsx +333 -148
  18. package/src/cortex-ui/renderer/components/retest/YakitSendPanel.tsx +411 -0
  19. package/src/cortex-ui/renderer/components/vuln/DirectoryPickerDialog.tsx +187 -0
  20. package/src/cortex-ui/renderer/components/vuln/ExportReportDialog.tsx +173 -59
  21. package/src/cortex-ui/renderer/components/vuln/VulnDetail.tsx +16 -4
  22. package/src/cortex-ui/renderer/i18n/messages/common.ts +2 -0
  23. package/src/cortex-ui/renderer/i18n/messages/fuzzer.ts +32 -0
  24. package/src/cortex-ui/renderer/i18n/messages/index.ts +5 -1
  25. package/src/cortex-ui/renderer/i18n/messages/miniapp.ts +392 -0
  26. package/src/cortex-ui/renderer/i18n/messages/retest.ts +146 -2
  27. package/src/cortex-ui/renderer/i18n/messages/vuln.ts +26 -6
  28. package/src/cortex-ui/renderer/lib/curl.ts +635 -0
  29. package/src/cortex-ui/renderer/lib/miniappApi.ts +459 -0
  30. package/src/cortex-ui/renderer/lib/navigation.ts +13 -1
  31. package/src/cortex-ui/renderer/lib/repeaterRaw.ts +102 -0
  32. package/src/cortex-ui/renderer/lib/repeaterStoreApi.ts +163 -0
  33. package/src/cortex-ui/renderer/lib/retestResponse.ts +64 -0
  34. package/src/cortex-ui/renderer/lib/transport/httpClient.ts +1 -1
  35. package/src/cortex-ui/renderer/pages/DashboardPage.tsx +3 -3
  36. package/src/cortex-ui/renderer/pages/FuzzerPage.tsx +212 -0
  37. package/src/cortex-ui/renderer/pages/MiniappPage.tsx +664 -0
  38. package/src/cortex-ui/shared/ipc.ts +3 -4
  39. package/src/cortex-ui/shared/types.ts +9 -0
  40. package/src/host/cortex/fs.js +72 -0
  41. package/src/host/cortex/index.js +49 -1
  42. package/src/host/cortex/repeater-store.js +411 -0
  43. package/src/host/cortex/repeater.js +15 -2
  44. package/src/host/cortex/settings.js +54 -0
  45. package/src/host/cortex/tools.js +2 -0
  46. package/src/host/cortex/vulns.js +36 -13
  47. package/src/host/miniapp/analyze.js +1078 -0
  48. package/src/host/miniapp/assets-import.js +294 -0
  49. package/src/host/miniapp/detect.js +426 -0
  50. package/src/host/miniapp/format.js +263 -0
  51. package/src/host/miniapp/roots.js +360 -0
  52. package/src/host/miniapp/routes.js +304 -0
  53. package/src/host/miniapp/service.js +559 -0
  54. package/src/host/miniapp/store.js +756 -0
  55. package/src/host/miniapp/tools.js +335 -0
  56. package/src/host/miniapp/unpack.js +1010 -0
  57. package/src/host/miniapp/wxapkg.js +416 -0
  58. package/src/host/reports-office.js +341 -0
@@ -0,0 +1,263 @@
1
+ import type { MouseEvent as ReactMouseEvent } from 'react'
2
+ import { Archive, Download, FileSearch, FolderOpen, Loader2, ScanSearch } from 'lucide-react'
3
+ import { Badge } from '@renderer/components/ui/badge'
4
+ import { Button } from '@renderer/components/ui/button'
5
+ import { formatBytes, formatDateTime } from '@renderer/lib/format'
6
+ import type { MiniappRow, MiniappStatus } from '@renderer/lib/miniappApi'
7
+ import { useI18n } from '@renderer/i18n'
8
+ import { cn } from '@renderer/lib/utils'
9
+
10
+ /** Quiet tinted chips matching the rest of the app's status badges. */
11
+ const STATUS_STYLES: Record<MiniappStatus, { badge: string; dot: string }> = {
12
+ detected: {
13
+ badge: 'border-sky-200/70 bg-sky-50 text-sky-700',
14
+ dot: 'bg-sky-500'
15
+ },
16
+ decrypting: {
17
+ badge: 'border-amber-200/70 bg-amber-50 text-amber-800',
18
+ dot: 'bg-amber-500 animate-pulse'
19
+ },
20
+ unpacked: {
21
+ badge: 'border-emerald-200/70 bg-emerald-50 text-emerald-700',
22
+ dot: 'bg-emerald-500'
23
+ },
24
+ unsupported: {
25
+ badge: 'border-orange-200/70 bg-orange-50 text-orange-700',
26
+ dot: 'bg-orange-500'
27
+ },
28
+ failed: {
29
+ badge: 'border-red-200/70 bg-red-50 text-red-700',
30
+ dot: 'bg-red-500'
31
+ }
32
+ }
33
+
34
+ export function MiniappStatusBadge({ status }: { status: MiniappStatus }) {
35
+ const { t } = useI18n()
36
+ const style = STATUS_STYLES[status] ?? STATUS_STYLES.failed
37
+ return (
38
+ <Badge className={cn(style.badge, 'whitespace-nowrap')}>
39
+ <span aria-hidden="true" className={cn('h-1.5 w-1.5 rounded-full', style.dot)} />
40
+ {t(`miniapp.status.${status}`)}
41
+ </Badge>
42
+ )
43
+ }
44
+
45
+ export function MiniappFormatBadge({ format }: { format: string }) {
46
+ const { t } = useI18n()
47
+ const known = format === 'plain' || format === 'v1mmwx' || format === 'unknown'
48
+ return (
49
+ <Badge variant="outline" className="whitespace-nowrap font-mono text-[10px]">
50
+ {t(`miniapp.format.${known ? format : 'unknown'}`)}
51
+ </Badge>
52
+ )
53
+ }
54
+
55
+ export type MiniappCardAction = 'unpack' | 'analyze' | 'import' | 'open'
56
+
57
+ interface MiniappCardProps {
58
+ item: MiniappRow
59
+ /** An unpack/analyze/import request for this source is in flight. */
60
+ busy: boolean
61
+ busyAction: MiniappCardAction | null
62
+ onOpen: (item: MiniappRow) => void
63
+ onUnpack: (item: MiniappRow) => void
64
+ onAnalyze: (item: MiniappRow) => void
65
+ onImport: (item: MiniappRow) => void
66
+ onOpenDir: (item: MiniappRow) => void
67
+ }
68
+
69
+ /**
70
+ * One detected wxapkg source row: identity, version/size/format/status plus the
71
+ * five source actions. The row itself is a button-like surface that opens the
72
+ * detail sheet; the action buttons stop propagation so they never race it.
73
+ */
74
+ export function MiniappCard({
75
+ item,
76
+ busy,
77
+ busyAction,
78
+ onOpen,
79
+ onUnpack,
80
+ onAnalyze,
81
+ onImport,
82
+ onOpenDir
83
+ }: MiniappCardProps) {
84
+ const { t } = useI18n()
85
+ const unpacked = item.status === 'unpacked'
86
+ const error = (item.error ?? '').trim()
87
+ const failed = item.status === 'unsupported' || item.status === 'failed'
88
+ const versionLabel =
89
+ (item.version ?? '').trim().length > 0
90
+ ? t('miniapp.card.version', { version: item.version })
91
+ : t('miniapp.card.noVersion')
92
+ const versionCount = item.version_count ?? 1
93
+ const appName = (item.app_name ?? '').trim()
94
+ const kindLabel =
95
+ item.kind === 'plugin'
96
+ ? t('miniapp.kind.plugin')
97
+ : item.kind === 'library'
98
+ ? t('miniapp.kind.library')
99
+ : item.kind === 'unknown'
100
+ ? t('miniapp.kind.unknown')
101
+ : t('miniapp.unnamed')
102
+ const title = appName.length > 0 ? appName : kindLabel
103
+
104
+ const stop = (event: ReactMouseEvent) => event.stopPropagation()
105
+ const run = (event: ReactMouseEvent, handler: (item: MiniappRow) => void) => {
106
+ event.stopPropagation()
107
+ handler(item)
108
+ }
109
+
110
+ return (
111
+ <li className="border-b border-border last:border-b-0">
112
+ <div
113
+ data-testid="miniapp-row"
114
+ data-miniapp-id={item.id}
115
+ data-status={item.status}
116
+ role="button"
117
+ tabIndex={0}
118
+ aria-label={title}
119
+ onClick={() => onOpen(item)}
120
+ onKeyDown={(event) => {
121
+ if (event.key === 'Enter' || event.key === ' ') {
122
+ event.preventDefault()
123
+ onOpen(item)
124
+ }
125
+ }}
126
+ className="flex cursor-pointer flex-col gap-2 px-4 py-3 transition-colors duration-150 hover:bg-sidebar focus-visible:bg-accent-soft focus-visible:outline-none"
127
+ >
128
+ <div className="flex min-w-0 flex-wrap items-center gap-2">
129
+ <span className="min-w-0 truncate text-[12.5px] font-medium text-foreground">{title}</span>
130
+ <MiniappStatusBadge status={item.status} />
131
+ <MiniappFormatBadge format={item.format} />
132
+ </div>
133
+
134
+ <div className="flex flex-wrap items-center gap-x-3 gap-y-1 font-mono text-[10.5px] text-muted-foreground">
135
+ {item.appid !== title ? <span className="truncate">{item.appid}</span> : null}
136
+ <span className="tabular-nums">{versionLabel}</span>
137
+ {versionCount > 1 ? (
138
+ <span className="tabular-nums">{t('miniapp.card.versions', { count: versionCount })}</span>
139
+ ) : null}
140
+ {(item.source_count ?? 1) > 1 ? (
141
+ <span className="tabular-nums" title={item.package_dir}>
142
+ {t('miniapp.card.copies', { count: item.source_count })}
143
+ </span>
144
+ ) : null}
145
+ <span className="tabular-nums">{formatBytes(item.size_bytes)}</span>
146
+ {item.file_count > 0 ? (
147
+ <span className="tabular-nums">{t('miniapp.card.files', { count: item.file_count })}</span>
148
+ ) : null}
149
+ <span className="tabular-nums">{t('miniapp.card.updated', { time: formatDateTime(item.updated_at) })}</span>
150
+ <span className="min-w-0 truncate" title={item.package_dir}>
151
+ {item.root_kind}
152
+ </span>
153
+ </div>
154
+
155
+ {failed && error.length > 0 ? (
156
+ <p
157
+ role="alert"
158
+ className={
159
+ item.status === 'unsupported'
160
+ ? 'break-all rounded-md border border-amber-200 bg-amber-50 px-2 py-1 text-[10.5px] leading-relaxed text-amber-800'
161
+ : 'break-all rounded-md border border-red-200 bg-red-50 px-2 py-1 text-[10.5px] leading-relaxed text-red-700'
162
+ }
163
+ >
164
+ {t(item.status === 'unsupported' ? 'miniapp.card.unsupportedLabel' : 'miniapp.card.errorLabel', {
165
+ error
166
+ })}
167
+ </p>
168
+ ) : null}
169
+
170
+ <div
171
+ className="flex flex-wrap items-center gap-1.5"
172
+ onClick={stop}
173
+ onKeyDown={(event) => event.stopPropagation()}
174
+ >
175
+ <Button
176
+ variant="outline"
177
+ size="xs"
178
+ data-testid="miniapp-unpack"
179
+ data-miniapp-id={item.id}
180
+ disabled={busy}
181
+ onClick={(event) => run(event, onUnpack)}
182
+ className="gap-1.5"
183
+ >
184
+ {busy && busyAction === 'unpack' ? (
185
+ <Loader2 aria-hidden="true" className="h-3 w-3 animate-spin" />
186
+ ) : (
187
+ <Archive aria-hidden="true" className="h-3 w-3" />
188
+ )}
189
+ {busy && busyAction === 'unpack'
190
+ ? t('miniapp.action.unpacking')
191
+ : t('miniapp.action.unpack')}
192
+ </Button>
193
+ <Button
194
+ variant="outline"
195
+ size="xs"
196
+ data-testid="miniapp-browse"
197
+ data-miniapp-id={item.id}
198
+ disabled={!unpacked}
199
+ onClick={(event) => run(event, onOpen)}
200
+ className="gap-1.5"
201
+ >
202
+ <FileSearch aria-hidden="true" className="h-3 w-3" />
203
+ {t('miniapp.action.browse')}
204
+ </Button>
205
+ <Button
206
+ variant="outline"
207
+ size="xs"
208
+ data-testid="miniapp-analyze"
209
+ data-miniapp-id={item.id}
210
+ disabled={!unpacked || busy}
211
+ onClick={(event) => run(event, onAnalyze)}
212
+ className="gap-1.5"
213
+ >
214
+ {busy && busyAction === 'analyze' ? (
215
+ <Loader2 aria-hidden="true" className="h-3 w-3 animate-spin" />
216
+ ) : (
217
+ <ScanSearch aria-hidden="true" className="h-3 w-3" />
218
+ )}
219
+ {busy && busyAction === 'analyze'
220
+ ? t('miniapp.action.analyzing')
221
+ : t('miniapp.action.analyze')}
222
+ </Button>
223
+ <Button
224
+ variant="outline"
225
+ size="xs"
226
+ data-testid="miniapp-import"
227
+ data-miniapp-id={item.id}
228
+ disabled={!unpacked || busy}
229
+ onClick={(event) => run(event, onImport)}
230
+ className="gap-1.5"
231
+ >
232
+ {busy && busyAction === 'import' ? (
233
+ <Loader2 aria-hidden="true" className="h-3 w-3 animate-spin" />
234
+ ) : (
235
+ <Download aria-hidden="true" className="h-3 w-3" />
236
+ )}
237
+ {busy && busyAction === 'import'
238
+ ? t('miniapp.action.importing')
239
+ : t('miniapp.action.import')}
240
+ </Button>
241
+ <Button
242
+ variant="ghost"
243
+ size="xs"
244
+ data-testid="miniapp-open"
245
+ data-miniapp-id={item.id}
246
+ disabled={!unpacked || busy}
247
+ onClick={(event) => run(event, onOpenDir)}
248
+ className="gap-1.5"
249
+ >
250
+ {busy && busyAction === 'open' ? (
251
+ <Loader2 aria-hidden="true" className="h-3 w-3 animate-spin" />
252
+ ) : (
253
+ <FolderOpen aria-hidden="true" className="h-3 w-3" />
254
+ )}
255
+ {busy && busyAction === 'open'
256
+ ? t('miniapp.action.opening')
257
+ : t('miniapp.action.openDir')}
258
+ </Button>
259
+ </div>
260
+ </div>
261
+ </li>
262
+ )
263
+ }