@checkstack/ui 1.8.1 → 1.8.3
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/CHANGELOG.md +62 -0
- package/package.json +4 -4
- package/src/components/LinksEditor.tsx +69 -34
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,67 @@
|
|
|
1
1
|
# @checkstack/ui
|
|
2
2
|
|
|
3
|
+
## 1.8.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1909a61: Address open CodeQL code-scanning findings:
|
|
8
|
+
|
|
9
|
+
- **`@checkstack/ui` (`LinksEditor`)**: validate URL scheme on render and on
|
|
10
|
+
add; only `http:` / `https:` URLs are accepted, defeating stored XSS via
|
|
11
|
+
`javascript:` / `data:` schemes in user-supplied hotlinks
|
|
12
|
+
(`js/xss-through-dom`).
|
|
13
|
+
- **`@checkstack/backend-api` (`markdownToPlainText`)**: decode HTML entities
|
|
14
|
+
before stripping tags, then strip tags in a loop until the output
|
|
15
|
+
stabilizes. Decoding `&` last avoids reintroducing tag delimiters
|
|
16
|
+
via `<` round-trips (`js/double-escaping`,
|
|
17
|
+
`js/incomplete-multi-character-sanitization`).
|
|
18
|
+
- **`@checkstack/backend` (`createScopedWsRegistry`)**: drop the
|
|
19
|
+
identity-replacement on the path suffix; the leading-slash invariant
|
|
20
|
+
is documented on `WebSocketRouteRegistry` (`js/identity-replacement`).
|
|
21
|
+
|
|
22
|
+
## 1.8.2
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- b627562: Bump direct and transitive dependencies to clear MEDIUM-severity advisories
|
|
27
|
+
that Trivy now surfaces alongside CRITICAL/HIGH.
|
|
28
|
+
|
|
29
|
+
Direct version bumps in package.json:
|
|
30
|
+
|
|
31
|
+
- `@checkstack/catalog-backend`, `@checkstack/gitops-backend`,
|
|
32
|
+
`@checkstack/healthcheck-frontend`: `uuid` `^13.0.0` → `^14.0.0`
|
|
33
|
+
(GHSA-w5hq-g745-h8pq, missing buffer bounds check in v3/v5/v6). Also
|
|
34
|
+
dropped the now-redundant `@types/uuid` devDependency — uuid 14 ships
|
|
35
|
+
its own types and the npm `@types/uuid` package is a stub.
|
|
36
|
+
- `@checkstack/gitops-backend`: `yaml` `^2.7.0` → `^2.8.3`
|
|
37
|
+
(GHSA-48c2-rrv3-qjmp, stack overflow on deeply nested collections).
|
|
38
|
+
- `@checkstack/dev-server`: `vite` `^5.4.0` → `^8.0.12`
|
|
39
|
+
(GHSA-4w7w-66w2-5vf9, path traversal in optimized-deps `.map` handling)
|
|
40
|
+
and `@vitejs/plugin-react` `^4.3.4` → `^6.0.1` to stay inside the new
|
|
41
|
+
vite peer range.
|
|
42
|
+
|
|
43
|
+
Root `overrides` / `resolutions` to bypass transitive pins that block the
|
|
44
|
+
walk:
|
|
45
|
+
|
|
46
|
+
- `dompurify` `^3.4.3` — `monaco-editor@0.55.1` pins `dompurify@3.2.7`
|
|
47
|
+
exactly, so the only way to pick up the eight DOMPurify XSS / prototype
|
|
48
|
+
pollution advisories (GHSA-v2wj-7wpq-c8vv et al.) is an override.
|
|
49
|
+
Affects `@checkstack/ui`, which is the only consumer of monaco.
|
|
50
|
+
- `uuid` `^14.0.0` — also forces `bullmq`'s nested `uuid@11.1.0`
|
|
51
|
+
(vulnerable per GHSA-w5hq-g745-h8pq) to the patched line. Affects
|
|
52
|
+
`@checkstack/queue-bullmq-backend`.
|
|
53
|
+
- `yaml` `^2.9.0` — covers transitive resolutions that would otherwise
|
|
54
|
+
pin pre-2.8.3 yaml.
|
|
55
|
+
|
|
56
|
+
The CI image scan (`.github/workflows/pr-checks.yml`) and the local
|
|
57
|
+
`bun run audit:*` helper now include `MEDIUM` alongside `CRITICAL,HIGH`,
|
|
58
|
+
so future MEDIUM regressions fail the pipeline. The production Dockerfile
|
|
59
|
+
also strips vendored `test/`, `tests/`, `__tests__/`, `benchmark/`,
|
|
60
|
+
`benchmarks/`, `example/` and `examples/` folders from `node_modules`
|
|
61
|
+
before the runtime stage — those tarball artefacts ship their own
|
|
62
|
+
nested `package.json` (`benchmark`, `tedious-benchmarks`, etc.) which
|
|
63
|
+
Trivy was scanning as if they were real packages.
|
|
64
|
+
|
|
3
65
|
## 1.8.1
|
|
4
66
|
|
|
5
67
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@checkstack/ui",
|
|
3
|
-
"version": "1.8.
|
|
3
|
+
"version": "1.8.3",
|
|
4
4
|
"license": "Elastic-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@checkstack/common": "0.
|
|
9
|
-
"@checkstack/frontend-api": "0.5.
|
|
8
|
+
"@checkstack/common": "0.10.0",
|
|
9
|
+
"@checkstack/frontend-api": "0.5.1",
|
|
10
10
|
"@monaco-editor/react": "^4.7.0",
|
|
11
11
|
"@radix-ui/react-accordion": "^1.2.12",
|
|
12
12
|
"@radix-ui/react-dialog": "^1.1.15",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"tailwind-merge": "^2.2.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@checkstack/scripts": "0.3.
|
|
33
|
+
"@checkstack/scripts": "0.3.2",
|
|
34
34
|
"@checkstack/test-utils-frontend": "0.0.5",
|
|
35
35
|
"@checkstack/tsconfig": "0.0.7",
|
|
36
36
|
"@storybook/addon-a11y": "^10.3.6",
|
|
@@ -10,6 +10,24 @@ export interface HotLink {
|
|
|
10
10
|
url: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// Parse a user-supplied URL and return its canonicalized form ONLY when the
|
|
14
|
+
// scheme is `http:` / `https:`. Returns `undefined` for any other scheme
|
|
15
|
+
// (`javascript:`, `data:`, `vbscript:`, etc.) so callers can refuse to
|
|
16
|
+
// render an anchor at all. The returned string is `URL.toString()` — i.e.
|
|
17
|
+
// a re-serialized URL — so taint analysis sees a fresh, sanitized value
|
|
18
|
+
// rather than the raw input flowing through. CodeQL js/xss-through-dom.
|
|
19
|
+
function safeHref(raw: string): string | undefined {
|
|
20
|
+
try {
|
|
21
|
+
const parsed = new URL(raw);
|
|
22
|
+
if (parsed.protocol === "http:" || parsed.protocol === "https:") {
|
|
23
|
+
return parsed.toString();
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
} catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
13
31
|
export interface LinksEditorProps<T extends HotLink> {
|
|
14
32
|
/** Currently attached links. */
|
|
15
33
|
links: T[];
|
|
@@ -60,6 +78,11 @@ export function LinksEditor<T extends HotLink>({
|
|
|
60
78
|
setError("Must be a valid URL (include http:// or https://)");
|
|
61
79
|
return;
|
|
62
80
|
}
|
|
81
|
+
const parsed = new URL(trimmedUrl);
|
|
82
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
83
|
+
setError("Only http:// and https:// URLs are allowed");
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
63
86
|
setError(undefined);
|
|
64
87
|
await onAdd({ label: label.trim() || undefined, url: trimmedUrl });
|
|
65
88
|
setLabel("");
|
|
@@ -77,42 +100,54 @@ export function LinksEditor<T extends HotLink>({
|
|
|
77
100
|
|
|
78
101
|
{links.length > 0 ? (
|
|
79
102
|
<div className="border rounded-lg divide-y">
|
|
80
|
-
{links.map((link) =>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<div className="min-w-0">
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
103
|
+
{links.map((link) => {
|
|
104
|
+
const href = safeHref(link.url);
|
|
105
|
+
return (
|
|
106
|
+
<div
|
|
107
|
+
key={link.id}
|
|
108
|
+
className="flex items-center justify-between p-3 gap-2"
|
|
109
|
+
>
|
|
110
|
+
<div className="flex items-center gap-2 min-w-0 flex-1">
|
|
111
|
+
<ExternalLink className="h-4 w-4 text-muted-foreground shrink-0" />
|
|
112
|
+
<div className="min-w-0">
|
|
113
|
+
{href ? (
|
|
114
|
+
<a
|
|
115
|
+
href={href}
|
|
116
|
+
target="_blank"
|
|
117
|
+
rel="noopener noreferrer"
|
|
118
|
+
className="text-sm text-primary hover:underline truncate block"
|
|
119
|
+
>
|
|
120
|
+
{link.label ?? link.url}
|
|
121
|
+
</a>
|
|
122
|
+
) : (
|
|
123
|
+
<span
|
|
124
|
+
className="text-sm text-muted-foreground truncate block"
|
|
125
|
+
title="Unsafe URL scheme — link disabled"
|
|
126
|
+
>
|
|
127
|
+
{link.label ?? link.url}
|
|
128
|
+
</span>
|
|
129
|
+
)}
|
|
130
|
+
{link.label && (
|
|
131
|
+
<span className="text-xs text-muted-foreground truncate block">
|
|
132
|
+
{link.url}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
135
|
+
</div>
|
|
101
136
|
</div>
|
|
137
|
+
{canManage && (
|
|
138
|
+
<Button
|
|
139
|
+
variant="ghost"
|
|
140
|
+
size="sm"
|
|
141
|
+
onClick={() => void onRemove(link)}
|
|
142
|
+
disabled={busy}
|
|
143
|
+
aria-label="Remove link"
|
|
144
|
+
>
|
|
145
|
+
<Trash2 className="h-4 w-4" />
|
|
146
|
+
</Button>
|
|
147
|
+
)}
|
|
102
148
|
</div>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
variant="ghost"
|
|
106
|
-
size="sm"
|
|
107
|
-
onClick={() => void onRemove(link)}
|
|
108
|
-
disabled={busy}
|
|
109
|
-
aria-label="Remove link"
|
|
110
|
-
>
|
|
111
|
-
<Trash2 className="h-4 w-4" />
|
|
112
|
-
</Button>
|
|
113
|
-
)}
|
|
114
|
-
</div>
|
|
115
|
-
))}
|
|
149
|
+
);
|
|
150
|
+
})}
|
|
116
151
|
</div>
|
|
117
152
|
) : (
|
|
118
153
|
<p className="text-sm text-muted-foreground">No links attached</p>
|