@geminilight/mindos 0.6.71 → 0.6.73

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.
Files changed (227) hide show
  1. package/_standalone/.mindos-build-version +1 -1
  2. package/_standalone/.next/BUILD_ID +1 -1
  3. package/_standalone/.next/app-path-routes-manifest.json +27 -27
  4. package/_standalone/.next/build-manifest.json +3 -3
  5. package/_standalone/.next/cache/.previewinfo +1 -1
  6. package/_standalone/.next/cache/.rscinfo +1 -1
  7. package/_standalone/.next/cache/config.json +3 -3
  8. package/_standalone/.next/prerender-manifest.json +3 -3
  9. package/_standalone/.next/react-loadable-manifest.json +4 -4
  10. package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
  11. package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  12. package/_standalone/.next/server/app/_global-error.html +2 -2
  13. package/_standalone/.next/server/app/_global-error.rsc +1 -1
  14. package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  15. package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  16. package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  17. package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  18. package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  19. package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  20. package/_standalone/.next/server/app/_not-found/page.js +1 -1
  21. package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
  24. package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
  25. package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
  26. package/_standalone/.next/server/app/agents/page.js +1 -1
  27. package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
  28. package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
  29. package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
  30. package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
  31. package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
  32. package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
  33. package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
  34. package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
  35. package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
  36. package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
  37. package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
  38. package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
  39. package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
  40. package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
  41. package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
  42. package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
  43. package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
  44. package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
  45. package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
  46. package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
  47. package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
  48. package/_standalone/.next/server/app/api/channels/verify/route_client-reference-manifest.js +1 -1
  49. package/_standalone/.next/server/app/api/connect/route_client-reference-manifest.js +1 -1
  50. package/_standalone/.next/server/app/api/embedding/route_client-reference-manifest.js +1 -1
  51. package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
  52. package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
  53. package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
  54. package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
  55. package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
  56. package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
  57. package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
  58. package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
  59. package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
  60. package/_standalone/.next/server/app/api/im/activity/route_client-reference-manifest.js +1 -1
  61. package/_standalone/.next/server/app/api/im/config/route_client-reference-manifest.js +1 -1
  62. package/_standalone/.next/server/app/api/im/status/route_client-reference-manifest.js +1 -1
  63. package/_standalone/.next/server/app/api/im/test/route_client-reference-manifest.js +1 -1
  64. package/_standalone/.next/server/app/api/im/webhook/feishu/route_client-reference-manifest.js +1 -1
  65. package/_standalone/.next/server/app/api/im/webhook-status/route_client-reference-manifest.js +1 -1
  66. package/_standalone/.next/server/app/api/inbox/clip/route_client-reference-manifest.js +1 -1
  67. package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
  68. package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
  69. package/_standalone/.next/server/app/api/lint/route_client-reference-manifest.js +1 -1
  70. package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
  71. package/_standalone/.next/server/app/api/mcp/direct-tools/route_client-reference-manifest.js +1 -1
  72. package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
  73. package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
  74. package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
  75. package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
  76. package/_standalone/.next/server/app/api/mcp/tools/route_client-reference-manifest.js +1 -1
  77. package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
  78. package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
  79. package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
  80. package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  81. package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
  82. package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
  83. package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
  84. package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
  85. package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
  86. package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
  87. package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
  88. package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
  89. package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
  90. package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
  91. package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
  92. package/_standalone/.next/server/app/api/space-overview/route_client-reference-manifest.js +1 -1
  93. package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
  94. package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
  95. package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
  96. package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  97. package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
  98. package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
  99. package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
  100. package/_standalone/.next/server/app/changelog/page.js +1 -1
  101. package/_standalone/.next/server/app/changelog/page.js.nft.json +1 -1
  102. package/_standalone/.next/server/app/changelog/page_client-reference-manifest.js +1 -1
  103. package/_standalone/.next/server/app/changes/page.js +1 -1
  104. package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
  105. package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
  106. package/_standalone/.next/server/app/echo/[segment]/page.js +1 -1
  107. package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
  108. package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
  109. package/_standalone/.next/server/app/echo/page.js +1 -1
  110. package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
  111. package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
  112. package/_standalone/.next/server/app/explore/page.js +1 -1
  113. package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
  114. package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
  115. package/_standalone/.next/server/app/help/page.js +1 -1
  116. package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
  117. package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
  118. package/_standalone/.next/server/app/inbox/history/page.js +1 -1
  119. package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
  120. package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
  121. package/_standalone/.next/server/app/login/page.js +1 -1
  122. package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
  123. package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
  124. package/_standalone/.next/server/app/page.js +1 -1
  125. package/_standalone/.next/server/app/page.js.nft.json +1 -1
  126. package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  127. package/_standalone/.next/server/app/setup/page.js +1 -1
  128. package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
  129. package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
  130. package/_standalone/.next/server/app/trash/page.js +2 -2
  131. package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
  132. package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
  133. package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
  134. package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
  135. package/_standalone/.next/server/app-paths-manifest.json +27 -27
  136. package/_standalone/.next/server/chunks/{3311.js → 2449.js} +2 -2
  137. package/_standalone/.next/server/chunks/5299.js +1 -1
  138. package/_standalone/.next/server/chunks/6022.js +34 -34
  139. package/_standalone/.next/server/middleware-build-manifest.js +1 -1
  140. package/_standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  141. package/_standalone/.next/server/pages/500.html +2 -2
  142. package/_standalone/.next/server/server-reference-manifest.js +1 -1
  143. package/_standalone/.next/server/server-reference-manifest.json +1 -1
  144. package/_standalone/.next/static/chunks/{7143.879daa87569c5b02.js → 4094.09364c01df411380.js} +1 -1
  145. package/_standalone/.next/static/chunks/{5795.d9099a1afecd6047.js → 5331.c89084fd7f67887d.js} +2 -2
  146. package/_standalone/.next/static/chunks/app/{layout-a344709b8447be75.js → layout-fcbde5bee626d21a.js} +63 -63
  147. package/_standalone/.next/static/chunks/app/trash/page-e623ff0ab35de002.js +1 -0
  148. package/_standalone/.next/static/chunks/app/view/[...path]/page-49c4eff6ffdb5168.js +12 -0
  149. package/_standalone/.next/static/chunks/{webpack-2f2787d3469d3df1.js → webpack-dc486b68118d1328.js} +1 -1
  150. package/_standalone/.next/trace +72 -72
  151. package/_standalone/package-lock.json +2 -2
  152. package/_standalone/package.json +1 -1
  153. package/app/package.json +1 -1
  154. package/package.json +1 -1
  155. package/_standalone/.next/static/chunks/app/trash/page-0907fdd06a4467de.js +0 -1
  156. package/_standalone/.next/static/chunks/app/view/[...path]/page-f53ce199b4a4bbb5.js +0 -12
  157. package/browser-extension/README.md +0 -160
  158. package/browser-extension/build.mjs +0 -63
  159. package/browser-extension/extension/background/service-worker.js +0 -1
  160. package/browser-extension/extension/content/extractor.js +0 -2
  161. package/browser-extension/extension/icons/icon-128.png +0 -0
  162. package/browser-extension/extension/icons/icon-128.svg +0 -4
  163. package/browser-extension/extension/icons/icon-16.png +0 -0
  164. package/browser-extension/extension/icons/icon-16.svg +0 -4
  165. package/browser-extension/extension/icons/icon-32.png +0 -0
  166. package/browser-extension/extension/icons/icon-32.svg +0 -4
  167. package/browser-extension/extension/icons/icon-48.png +0 -0
  168. package/browser-extension/extension/icons/icon-48.svg +0 -4
  169. package/browser-extension/extension/manifest.json +0 -47
  170. package/browser-extension/extension/popup/popup.css +0 -510
  171. package/browser-extension/extension/popup/popup.html +0 -128
  172. package/browser-extension/extension/popup/popup.js +0 -73
  173. package/browser-extension/package-lock.json +0 -617
  174. package/browser-extension/package.json +0 -21
  175. package/browser-extension/scripts/gen-icons.sh +0 -38
  176. package/browser-extension/src/background/service-worker.ts +0 -27
  177. package/browser-extension/src/content/extractor.ts +0 -44
  178. package/browser-extension/src/icons/icon-128.png +0 -0
  179. package/browser-extension/src/icons/icon-128.svg +0 -4
  180. package/browser-extension/src/icons/icon-16.png +0 -0
  181. package/browser-extension/src/icons/icon-16.svg +0 -4
  182. package/browser-extension/src/icons/icon-32.png +0 -0
  183. package/browser-extension/src/icons/icon-32.svg +0 -4
  184. package/browser-extension/src/icons/icon-48.png +0 -0
  185. package/browser-extension/src/icons/icon-48.svg +0 -4
  186. package/browser-extension/src/lib/api.ts +0 -146
  187. package/browser-extension/src/lib/markdown.ts +0 -68
  188. package/browser-extension/src/lib/storage.ts +0 -37
  189. package/browser-extension/src/lib/types.ts +0 -42
  190. package/browser-extension/src/manifest.json +0 -47
  191. package/browser-extension/src/popup/popup.css +0 -510
  192. package/browser-extension/src/popup/popup.html +0 -128
  193. package/browser-extension/src/popup/popup.ts +0 -416
  194. package/browser-extension/tsconfig.json +0 -16
  195. package/tests/e2e/README.md +0 -25
  196. package/tests/e2e/navigation.spec.ts +0 -14
  197. package/tests/e2e/playwright.config.ts +0 -14
  198. package/tests/integration/README.md +0 -25
  199. package/tests/integration/mcp-contract.test.ts +0 -57
  200. package/tests/integration/mcp-transport.test.ts +0 -361
  201. package/tests/integration/package-lock.json +0 -1463
  202. package/tests/integration/package.json +0 -8
  203. package/tests/integration/vitest.config.ts +0 -11
  204. package/tests/security-hardening.test.ts +0 -456
  205. package/tests/unit/build-integrity.test.ts +0 -137
  206. package/tests/unit/cli-build.test.ts +0 -180
  207. package/tests/unit/cli-config.test.ts +0 -257
  208. package/tests/unit/cli-mcp-install-toml.test.ts +0 -586
  209. package/tests/unit/cli-mcp-install.test.ts +0 -123
  210. package/tests/unit/cli-mcp-stdio-default.test.ts +0 -180
  211. package/tests/unit/cli-modules-load.test.ts +0 -64
  212. package/tests/unit/cli-port.test.ts +0 -87
  213. package/tests/unit/cli-skill-auto-copy.test.ts +0 -260
  214. package/tests/unit/cli-smoke.test.ts +0 -88
  215. package/tests/unit/cli-uninstall.test.ts +0 -218
  216. package/tests/unit/cli-update-root.test.ts +0 -89
  217. package/tests/unit/cli-user-flow-sim.test.ts +0 -506
  218. package/tests/unit/cli-wait-hint.test.ts +0 -86
  219. package/tests/unit/custom-agents.test.ts +0 -478
  220. package/tests/unit/dep-safety.test.ts +0 -126
  221. package/tests/unit/detect-system-lang.test.ts +0 -122
  222. package/tests/unit/mcp-build.test.ts +0 -162
  223. package/tests/unit/setup-needs-restart.test.ts +0 -139
  224. package/tests/unit/stop-restart.test.ts +0 -393
  225. package/tests/unit/vitest.config.ts +0 -8
  226. /package/_standalone/.next/static/{w5bqzZbd2_vdoPRB0JQ_I → Dn8EHqUedSzanCfrM8WWS}/_buildManifest.js +0 -0
  227. /package/_standalone/.next/static/{w5bqzZbd2_vdoPRB0JQ_I → Dn8EHqUedSzanCfrM8WWS}/_ssgManifest.js +0 -0
@@ -1,8 +0,0 @@
1
- {
2
- "name": "mindos-integration-tests",
3
- "private": true,
4
- "type": "module",
5
- "devDependencies": {
6
- "vitest": "^4.0.18"
7
- }
8
- }
@@ -1,11 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- include: ['**/*.test.ts'],
6
- testTimeout: 15_000,
7
- env: {
8
- MINDOS_URL: process.env.MINDOS_URL ?? 'http://localhost:3456',
9
- },
10
- },
11
- });
@@ -1,456 +0,0 @@
1
- /**
2
- * Security hardening test suite for MindOS update system.
3
- * Tests cover:
4
- * - Symlink attack prevention
5
- * - Path traversal prevention
6
- * - Race condition detection
7
- * - Atomic operation verification
8
- * - Data loss prevention
9
- */
10
-
11
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
12
- import path from 'path';
13
- import os from 'os';
14
- import {
15
- mkdirSync, writeFileSync, readFileSync, unlinkSync, symlinkSync,
16
- existsSync, statSync, rmdirSync, rmSync, realpathSync,
17
- } from 'fs';
18
- import {
19
- isSymlink, assertNotSymlink, assertNoSymlinksInPath,
20
- safeRmSync, safeMkdir, getSafeStats, assessDeletionRisk,
21
- } from '../../desktop/src/safe-rm';
22
- import {
23
- validateRuntimePath, getRuntimePaths, isValidDirName, sanitizeDirName,
24
- } from '../../desktop/src/safe-paths';
25
-
26
- describe('Security Hardening - Symlink Protection', () => {
27
- let tempDir: string;
28
-
29
- beforeEach(() => {
30
- tempDir = path.join(os.tmpdir(), `mindos-test-${Date.now()}`);
31
- mkdirSync(tempDir, { recursive: true });
32
- });
33
-
34
- afterEach(() => {
35
- try {
36
- rmSync(tempDir, { recursive: true, force: true });
37
- } catch { /* ignore */ }
38
- });
39
-
40
- describe('Symlink Detection', () => {
41
- it('should detect direct symlinks', () => {
42
- const targetDir = path.join(tempDir, 'target');
43
- const linkDir = path.join(tempDir, 'link');
44
-
45
- mkdirSync(targetDir);
46
- symlinkSync(targetDir, linkDir);
47
-
48
- expect(isSymlink(linkDir)).toBe(true);
49
- expect(isSymlink(targetDir)).toBe(false);
50
- });
51
-
52
- it('should return false for non-existent paths', () => {
53
- const nonExistent = path.join(tempDir, 'does-not-exist');
54
- expect(isSymlink(nonExistent)).toBe(false);
55
- });
56
-
57
- it('should detect symlinks pointing to files', () => {
58
- const file = path.join(tempDir, 'file.txt');
59
- const link = path.join(tempDir, 'link.txt');
60
-
61
- writeFileSync(file, 'content');
62
- symlinkSync(file, link);
63
-
64
- expect(isSymlink(link)).toBe(true);
65
- });
66
- });
67
-
68
- describe('Assertion: assertNotSymlink', () => {
69
- it('should throw when path is a symlink', () => {
70
- const targetDir = path.join(tempDir, 'target');
71
- const linkDir = path.join(tempDir, 'link');
72
-
73
- mkdirSync(targetDir);
74
- symlinkSync(targetDir, linkDir);
75
-
76
- expect(() => {
77
- assertNotSymlink(linkDir);
78
- }).toThrow(/SECURITY.*symlink/i);
79
- });
80
-
81
- it('should not throw for regular directories', () => {
82
- const regularDir = path.join(tempDir, 'regular');
83
- mkdirSync(regularDir);
84
-
85
- expect(() => {
86
- assertNotSymlink(regularDir);
87
- }).not.toThrow();
88
- });
89
-
90
- it('should not throw for non-existent paths', () => {
91
- const nonExistent = path.join(tempDir, 'does-not-exist');
92
- expect(() => {
93
- assertNotSymlink(nonExistent);
94
- }).not.toThrow();
95
- });
96
- });
97
-
98
- describe('Path Chain Symlink Check', () => {
99
- it('should detect symlinks in parent chain', () => {
100
- const realDir = path.join(tempDir, 'real');
101
- const symlinkParent = path.join(tempDir, 'symlink-parent');
102
- const childPath = path.join(symlinkParent, 'child');
103
-
104
- mkdirSync(realDir);
105
- symlinkSync(realDir, symlinkParent);
106
-
107
- expect(() => {
108
- assertNoSymlinksInPath(childPath, tempDir);
109
- }).toThrow(/SECURITY.*symlink/i);
110
- });
111
-
112
- it('should allow clean parent chains', () => {
113
- const dir1 = path.join(tempDir, 'dir1');
114
- const dir2 = path.join(dir1, 'dir2');
115
-
116
- mkdirSync(dir2, { recursive: true });
117
-
118
- expect(() => {
119
- assertNoSymlinksInPath(dir2, tempDir);
120
- }).not.toThrow();
121
- });
122
- });
123
- });
124
-
125
- describe('Security Hardening - Safe Deletion', () => {
126
- let tempDir: string;
127
-
128
- beforeEach(() => {
129
- tempDir = path.join(os.tmpdir(), `mindos-saferm-${Date.now()}`);
130
- mkdirSync(tempDir, { recursive: true });
131
- });
132
-
133
- afterEach(() => {
134
- try {
135
- rmSync(tempDir, { recursive: true, force: true });
136
- } catch { /* ignore */ }
137
- });
138
-
139
- describe('safeRmSync', () => {
140
- it('should refuse to delete symlinks', () => {
141
- const targetDir = path.join(tempDir, 'target');
142
- const linkDir = path.join(tempDir, 'link');
143
-
144
- mkdirSync(targetDir);
145
- symlinkSync(targetDir, linkDir);
146
-
147
- expect(() => {
148
- safeRmSync(linkDir);
149
- }).toThrow(/SECURITY.*symlink/i);
150
-
151
- // Verify link still exists
152
- expect(existsSync(linkDir)).toBe(true);
153
- });
154
-
155
- it('should safely delete regular directories', () => {
156
- const dir = path.join(tempDir, 'regular-dir');
157
- mkdirSync(dir);
158
- writeFileSync(path.join(dir, 'file.txt'), 'content');
159
-
160
- expect(() => {
161
- safeRmSync(dir, { recursive: true });
162
- }).not.toThrow();
163
-
164
- expect(existsSync(dir)).toBe(false);
165
- });
166
-
167
- it('should be idempotent for non-existent paths', () => {
168
- const nonExistent = path.join(tempDir, 'does-not-exist');
169
-
170
- expect(() => {
171
- safeRmSync(nonExistent);
172
- }).not.toThrow();
173
- });
174
-
175
- it('should refuse to delete non-directories without recursive flag', () => {
176
- const file = path.join(tempDir, 'file.txt');
177
- writeFileSync(file, 'content');
178
-
179
- // Should work for files
180
- expect(() => {
181
- safeRmSync(file);
182
- }).not.toThrow();
183
-
184
- expect(existsSync(file)).toBe(false);
185
- });
186
- });
187
-
188
- describe('safeMkdir', () => {
189
- it('should refuse to create in symlinked parents', () => {
190
- const realDir = path.join(tempDir, 'real');
191
- const symlinkParent = path.join(tempDir, 'symlink-parent');
192
- const newDir = path.join(symlinkParent, 'newdir');
193
-
194
- mkdirSync(realDir);
195
- symlinkSync(realDir, symlinkParent);
196
-
197
- expect(() => {
198
- safeMkdir(newDir);
199
- }).toThrow(/SECURITY.*symlink/i);
200
- });
201
-
202
- it('should create directories in clean paths', () => {
203
- const dir = path.join(tempDir, 'a', 'b', 'c');
204
-
205
- expect(() => {
206
- safeMkdir(dir);
207
- }).not.toThrow();
208
-
209
- expect(existsSync(dir)).toBe(true);
210
- });
211
- });
212
-
213
- describe('Risk Assessment', () => {
214
- it('should identify symlink risks', () => {
215
- const targetDir = path.join(tempDir, 'target');
216
- const linkDir = path.join(tempDir, 'link');
217
-
218
- mkdirSync(targetDir);
219
- symlinkSync(targetDir, linkDir);
220
-
221
- const risks = assessDeletionRisk(linkDir, tempDir);
222
- expect(risks.isSymlink).toBe(true);
223
- });
224
-
225
- it('should identify system path risks', () => {
226
- const externalPath = '/etc/passwd';
227
- const risks = assessDeletionRisk(externalPath, tempDir);
228
- expect(risks.isSystemPath).toBe(true);
229
- });
230
-
231
- it('should clean paths without risks', () => {
232
- const dir = path.join(tempDir, 'safe-dir');
233
- mkdirSync(dir);
234
-
235
- const risks = assessDeletionRisk(dir, tempDir);
236
- expect(risks.isSymlink).toBe(false);
237
- expect(risks.hasSymlinkParent).toBe(false);
238
- expect(risks.isSystemPath).toBe(false);
239
- });
240
- });
241
- });
242
-
243
- describe('Security Hardening - Path Validation', () => {
244
- const mockConfigDir = path.join(os.tmpdir(), '.mindos');
245
-
246
- beforeEach(() => {
247
- mkdirSync(mockConfigDir, { recursive: true });
248
- });
249
-
250
- afterEach(() => {
251
- try {
252
- rmSync(mockConfigDir, { recursive: true, force: true });
253
- } catch { /* ignore */ }
254
- });
255
-
256
- describe('Path Traversal Prevention', () => {
257
- it('should reject paths with ..', () => {
258
- expect(() => {
259
- validateRuntimePath('../../etc/passwd');
260
- }).toThrow(/path traversal/i);
261
- });
262
-
263
- it('should reject absolute paths outside .mindos', () => {
264
- expect(() => {
265
- validateRuntimePath('/etc/passwd');
266
- }).toThrow(/outside/i);
267
- });
268
-
269
- it('should accept safe subdirectories', () => {
270
- const safePath = 'runtime';
271
- expect(() => {
272
- validateRuntimePath(safePath);
273
- }).not.toThrow();
274
- });
275
-
276
- it('should reject null bytes', () => {
277
- expect(() => {
278
- validateRuntimePath('runtime\0etc/passwd');
279
- }).toThrow(/null byte/i);
280
- });
281
- });
282
-
283
- describe('Directory Name Validation', () => {
284
- it('should reject directory names with path separators', () => {
285
- expect(isValidDirName('dir/name')).toBe(false);
286
- expect(isValidDirName('dir\\name')).toBe(false);
287
- });
288
-
289
- it('should reject . and ..', () => {
290
- expect(isValidDirName('.')).toBe(false);
291
- expect(isValidDirName('..')).toBe(false);
292
- });
293
-
294
- it('should reject hidden directories (starting with .)', () => {
295
- expect(isValidDirName('.hidden')).toBe(false);
296
- });
297
-
298
- it('should accept normal directory names', () => {
299
- expect(isValidDirName('runtime')).toBe(true);
300
- expect(isValidDirName('runtime-old')).toBe(true);
301
- expect(isValidDirName('runtime-downloading')).toBe(true);
302
- });
303
- });
304
-
305
- describe('Directory Name Sanitization', () => {
306
- it('should sanitize path separators', () => {
307
- const sanitized = sanitizeDirName('dir/name');
308
- expect(sanitized).not.toContain('/');
309
- expect(isValidDirName(sanitized)).toBe(true);
310
- });
311
-
312
- it('should sanitize null bytes', () => {
313
- const sanitized = sanitizeDirName('name\0etc');
314
- expect(sanitized).not.toContain('\0');
315
- expect(isValidDirName(sanitized)).toBe(true);
316
- });
317
-
318
- it('should sanitize .. patterns', () => {
319
- const sanitized = sanitizeDirName('name..etc');
320
- expect(sanitized).toContain('__');
321
- expect(isValidDirName(sanitized)).toBe(true);
322
- });
323
-
324
- it('should reject if sanitization fails', () => {
325
- expect(() => {
326
- sanitizeDirName('\0\0\0');
327
- }).toThrow();
328
- });
329
- });
330
- });
331
-
332
- describe('Security Hardening - Atomic Operations', () => {
333
- let tempDir: string;
334
-
335
- beforeEach(() => {
336
- tempDir = path.join(os.tmpdir(), `mindos-atomic-${Date.now()}`);
337
- mkdirSync(tempDir, { recursive: true });
338
- });
339
-
340
- afterEach(() => {
341
- try {
342
- rmSync(tempDir, { recursive: true, force: true });
343
- } catch { /* ignore */ }
344
- });
345
-
346
- it('should handle update rollback scenario', () => {
347
- const runtimeDir = path.join(tempDir, 'runtime');
348
- const downloadDir = path.join(tempDir, 'runtime-downloading');
349
- const oldDir = path.join(tempDir, 'runtime-old');
350
-
351
- // Simulate current runtime
352
- mkdirSync(runtimeDir);
353
- writeFileSync(path.join(runtimeDir, 'version.txt'), '1.0.0');
354
-
355
- // Simulate new download
356
- mkdirSync(downloadDir);
357
- writeFileSync(path.join(downloadDir, 'version.txt'), '1.1.0');
358
-
359
- // Simulate atomic apply
360
- try {
361
- // Move old → backup
362
- if (existsSync(runtimeDir)) {
363
- if (existsSync(oldDir)) {
364
- safeRmSync(oldDir, { recursive: true });
365
- }
366
- // Simulate rename
367
- const tempBackup = path.join(tempDir, 'runtime-backup-temp');
368
- require('fs').renameSync(runtimeDir, tempBackup);
369
- require('fs').renameSync(tempBackup, oldDir);
370
- }
371
-
372
- // Move new → current
373
- require('fs').renameSync(downloadDir, runtimeDir);
374
-
375
- // Verify new runtime is in place
376
- expect(existsSync(runtimeDir)).toBe(true);
377
- expect(readFileSync(path.join(runtimeDir, 'version.txt'), 'utf-8')).toBe('1.1.0');
378
- } catch (err) {
379
- // On error, verify rollback
380
- expect(existsSync(runtimeDir)).toBe(true);
381
- expect(readFileSync(path.join(runtimeDir, 'version.txt'), 'utf-8')).toBe('1.0.0');
382
- }
383
- });
384
- });
385
-
386
- describe('End-to-End Security Scenarios', () => {
387
- let tempDir: string;
388
-
389
- beforeEach(() => {
390
- tempDir = path.join(os.tmpdir(), `mindos-e2e-${Date.now()}`);
391
- mkdirSync(tempDir, { recursive: true });
392
- });
393
-
394
- afterEach(() => {
395
- try {
396
- rmSync(tempDir, { recursive: true, force: true });
397
- } catch { /* ignore */ }
398
- });
399
-
400
- it('should prevent deletion via symlink to user data', () => {
401
- // Setup: Create user data directory with marker
402
- const userDataDir = path.join(tempDir, 'mind');
403
- const guardFile = path.join(userDataDir, '.mindos-guard');
404
- mkdirSync(userDataDir);
405
- writeFileSync(guardFile, 'USER DATA');
406
-
407
- // Attack: Create symlink pointing to user data
408
- const symlinkToUserData = path.join(tempDir, 'runtime-old');
409
- symlinkSync(userDataDir, symlinkToUserData);
410
-
411
- // Defense: Should refuse deletion
412
- expect(() => {
413
- assertNotSymlink(symlinkToUserData);
414
- }).toThrow();
415
-
416
- // Verify user data still exists
417
- expect(readFileSync(guardFile, 'utf-8')).toBe('USER DATA');
418
- });
419
-
420
- it('should prevent deletion via path traversal', () => {
421
- // Setup: Create structure
422
- const runtimeDir = path.join(tempDir, '.mindos', 'runtime');
423
- mkdirSync(runtimeDir, { recursive: true });
424
-
425
- // Attack: Try to validate malicious path
426
- const maliciousPath = path.join(
427
- tempDir,
428
- '.mindos',
429
- 'runtime',
430
- '../../../../etc/passwd'
431
- );
432
-
433
- // Defense: Should reject path traversal
434
- expect(() => {
435
- validateRuntimePath(maliciousPath);
436
- }).toThrow();
437
- });
438
-
439
- it('should maintain consistency on update failure', () => {
440
- // This test verifies the update system can recover from failures
441
- // Setup initial state
442
- const runtimeDir = path.join(tempDir, 'runtime');
443
- mkdirSync(runtimeDir);
444
- writeFileSync(path.join(runtimeDir, 'marker.txt'), 'original');
445
-
446
- // Simulate interrupted update
447
- // (Download starts but never completes)
448
- const downloadDir = path.join(tempDir, 'runtime-downloading');
449
- mkdirSync(downloadDir);
450
-
451
- // After cleanup, original should be intact
452
- safeRmSync(downloadDir, { recursive: true });
453
- expect(existsSync(runtimeDir)).toBe(true);
454
- expect(readFileSync(path.join(runtimeDir, 'marker.txt'), 'utf-8')).toBe('original');
455
- });
456
- });
@@ -1,137 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import fs from 'fs';
3
- import path from 'path';
4
-
5
- /**
6
- * Build integrity tests — ensure the production build output is self-consistent,
7
- * so users can actually load the GUI without broken CSS/JS references.
8
- *
9
- * These tests inspect the `.next` build output directly (no running server needed).
10
- * They catch the class of bug where HTML references assets with hash X but the
11
- * actual files have hash Y (e.g. after a partial rebuild or cache corruption).
12
- */
13
-
14
- const APP_DIR = path.resolve(__dirname, '..', '..', 'app');
15
- const NEXT_DIR = path.join(APP_DIR, '.next');
16
-
17
- // Skip all tests if no complete build output exists (e.g. local dev without build)
18
- const hasBuild = fs.existsSync(path.join(NEXT_DIR, 'build-manifest.json'));
19
-
20
- describe.skipIf(!hasBuild)('build integrity', () => {
21
- // ── Helpers ────────────────────────────────────────────────────────────────
22
-
23
- /** Recursively collect all files matching a predicate */
24
- function walkFiles(dir: string, filter: (f: string) => boolean): string[] {
25
- const results: string[] = [];
26
- if (!fs.existsSync(dir)) return results;
27
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
28
- const full = path.join(dir, entry.name);
29
- if (entry.isDirectory()) {
30
- results.push(...walkFiles(full, filter));
31
- } else if (filter(entry.name)) {
32
- results.push(full);
33
- }
34
- }
35
- return results;
36
- }
37
-
38
- /** Extract /_next/static/... references from file content */
39
- function extractStaticRefs(content: string): string[] {
40
- const refs: string[] = [];
41
- // Match /_next/static/chunks/xxx.css and /_next/static/chunks/xxx.js
42
- const pattern = /\/_next\/static\/[^\s"'`)]+?\.(css|js)/g;
43
- let m: RegExpExecArray | null;
44
- while ((m = pattern.exec(content)) !== null) {
45
- refs.push(m[0]);
46
- }
47
- return refs;
48
- }
49
-
50
- // ── Tests ──────────────────────────────────────────────────────────────────
51
-
52
- it('build output directory exists with required subdirs', () => {
53
- expect(fs.existsSync(path.join(NEXT_DIR, 'static'))).toBe(true);
54
- expect(fs.existsSync(path.join(NEXT_DIR, 'server'))).toBe(true);
55
- });
56
-
57
- it('has at least one CSS file in static output', () => {
58
- const cssFiles = walkFiles(path.join(NEXT_DIR, 'static'), f => f.endsWith('.css'));
59
- expect(cssFiles.length).toBeGreaterThanOrEqual(1);
60
- });
61
-
62
- it('has at least one JS chunk in static output', () => {
63
- const jsFiles = walkFiles(path.join(NEXT_DIR, 'static'), f => f.endsWith('.js'));
64
- expect(jsFiles.length).toBeGreaterThanOrEqual(1);
65
- });
66
-
67
- it('all /_next/static references in server output resolve to actual files', () => {
68
- // Scan server-rendered HTML/RSC files for /_next/static/... references
69
- const serverFiles = walkFiles(path.join(NEXT_DIR, 'server'), f =>
70
- f.endsWith('.html') || f.endsWith('.body') || f.endsWith('.rsc') || f.endsWith('.meta') || f.endsWith('.js')
71
- );
72
-
73
- const allRefs = new Set<string>();
74
- const missing: string[] = [];
75
-
76
- for (const file of serverFiles) {
77
- const content = fs.readFileSync(file, 'utf-8');
78
- for (const ref of extractStaticRefs(content)) {
79
- allRefs.add(ref);
80
- }
81
- }
82
-
83
- for (const ref of allRefs) {
84
- // /_next/static/chunks/xxx.css → .next/static/chunks/xxx.css
85
- const onDisk = path.join(NEXT_DIR, ref.replace(/^\/_next\//, ''));
86
- if (!fs.existsSync(onDisk)) {
87
- missing.push(ref);
88
- }
89
- }
90
-
91
- // If we found references, none should be missing
92
- if (allRefs.size > 0) {
93
- expect(missing).toEqual([]);
94
- }
95
- });
96
-
97
- it('CSS files are non-empty and contain valid content', () => {
98
- const cssFiles = walkFiles(path.join(NEXT_DIR, 'static'), f => f.endsWith('.css'));
99
- for (const file of cssFiles) {
100
- const stat = fs.statSync(file);
101
- expect(stat.size, `CSS file should not be empty: ${path.basename(file)}`).toBeGreaterThan(0);
102
- // Basic sanity: should contain CSS-like content (selectors, properties)
103
- const content = fs.readFileSync(file, 'utf-8');
104
- expect(
105
- content.includes('{') && content.includes('}'),
106
- `CSS file should contain valid CSS rules: ${path.basename(file)}`
107
- ).toBe(true);
108
- }
109
- });
110
-
111
- it('build manifest exists and is valid JSON', () => {
112
- const manifestPath = path.join(NEXT_DIR, 'build-manifest.json');
113
- expect(fs.existsSync(manifestPath)).toBe(true);
114
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
115
- expect(manifest).toHaveProperty('pages');
116
- });
117
-
118
- it('build manifest page assets all exist on disk', () => {
119
- const manifestPath = path.join(NEXT_DIR, 'build-manifest.json');
120
- if (!fs.existsSync(manifestPath)) return;
121
-
122
- const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
123
- const missing: string[] = [];
124
-
125
- for (const [page, assets] of Object.entries(manifest.pages || {})) {
126
- for (const asset of (assets as string[])) {
127
- // Assets in manifest are relative paths like _next/static/chunks/xxx.js
128
- const onDisk = path.join(NEXT_DIR, asset.replace(/^_next\//, ''));
129
- if (!fs.existsSync(onDisk)) {
130
- missing.push(`${page} → ${asset}`);
131
- }
132
- }
133
- }
134
-
135
- expect(missing).toEqual([]);
136
- });
137
- });