@descix/cli 1.0.12 → 1.0.13

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/bin/descix.js CHANGED
@@ -2537,6 +2537,11 @@ siteCommand
2537
2537
  return;
2538
2538
  }
2539
2539
 
2540
+ // 1b. Refuse to publish backend source, an agent's persona, credentials or workspace
2541
+ // state — BEFORE any token is requested, so a dry run refuses too (SiteDenyLint).
2542
+ const { assertSitePublishable } = await import('../lib/core/SiteDenyLint.js');
2543
+ assertSitePublishable(fileList.map((f) => f.path));
2544
+
2540
2545
  // 2. Request deploy token with file list
2541
2546
  const tokenResponse = await apiClient.invoke('get_site_deploy_token', {
2542
2547
  community_id: communityId,
@@ -0,0 +1,69 @@
1
+ /**
2
+ * What a site upload must never publish. Hard deny — the upload fails loud before any file
3
+ * leaves the machine, dry-run included.
4
+ *
5
+ * WHY (measured, 2026-09-18): egpt-godsworld's DEV site had been publicly serving, as static
6
+ * files, the app's persona (`assets/system_instructions.md`) and its whole backend source tree
7
+ * (`microservice/app.js`, `Dockerfile`, `services/*`, config examples). An earlier upload used a
8
+ * broad manifest, and `site upload` publishes whatever its manifest names. The KB sync already
9
+ * refuses private content (CorpusDenyLint); the site upload had no equivalent.
10
+ *
11
+ * A DIFFERENT PATTERN SET from the corpus, on purpose: the corpus lint is about editorial
12
+ * publishability (drafts, patents, test dumps), and a site legitimately ships some of those. This
13
+ * set is about what is private at RUNTIME — backend source, an agent's persona, credentials,
14
+ * workspace and tooling state. The MATCHER is shared (CorpusDenyLint.matchPathPattern), so there
15
+ * is one definition of how a pattern meets a path.
16
+ *
17
+ * No exemption flag: every class here is repaired by not naming the file in the manifest, which
18
+ * is always possible. An escape hatch would be the one path the next leak takes.
19
+ */
20
+
21
+ import { matchPathPattern } from './CorpusDenyLint.js';
22
+
23
+ export const SITE_DENY_PATH_PATTERNS = [
24
+ { id: 1, class: 'app-source', patterns: ['**/microservice/**', 'microservice'],
25
+ reason: 'backend source — an app\'s microservice is deployed as a service, never served as files' },
26
+ { id: 2, class: 'agent-persona', patterns: ['system_instructions.md'],
27
+ reason: 'the agent\'s persona / system instructions — its prompt is not a public asset' },
28
+ { id: 3, class: 'credential', caseFold: true,
29
+ patterns: ['wallet*.json', '.env', '.env.*', 'dev-overrides*.json', 'secret*.json', '*.pem', '*.key',
30
+ 'service-account*.json', 'credentials*.json', 'id_rsa*', '*.p12'],
31
+ reason: 'credential or credential-shaped config (a placeholder copy teaches the real layout)' },
32
+ { id: 4, class: 'workspace-state', patterns: ['**/.descix/**', '.descix'],
33
+ reason: 'DeSciX workspace / session state' },
34
+ { id: 5, class: 'tooling-state', patterns: ['**/.claude/**', '**/.git/**', '.claude', '.git'],
35
+ reason: 'agent or VCS internals' },
36
+ ];
37
+
38
+ /**
39
+ * Every published path that matches a deny class.
40
+ * @param {string[]} publishedPaths - paths relative to the site root, as they would be served
41
+ * @returns {Array<{path:string, class:string, glob:string, reason:string}>}
42
+ */
43
+ export function lintSiteFiles(publishedPaths) {
44
+ const violations = [];
45
+ for (const p of publishedPaths) {
46
+ for (const pattern of SITE_DENY_PATH_PATTERNS) {
47
+ const glob = matchPathPattern(p, pattern);
48
+ if (glob) {
49
+ violations.push({ path: p, class: pattern.class, glob, reason: pattern.reason });
50
+ break;
51
+ }
52
+ }
53
+ }
54
+ return violations;
55
+ }
56
+
57
+ /** Throw, naming every offending file and the repair, when any path is denied. */
58
+ export function assertSitePublishable(publishedPaths) {
59
+ const violations = lintSiteFiles(publishedPaths);
60
+ if (violations.length === 0) return;
61
+ const lines = violations.map((v) => ` - ${v.path} [${v.class}] ${v.reason}`);
62
+ const err = new Error(
63
+ `site upload REFUSED: ${violations.length} file(s) must never be publicly served:\n${lines.join('\n')}\n` +
64
+ 'Nothing was uploaded. Remove these from the site manifest\'s sources (.descix/manifests/site.json) ' +
65
+ 'or from the build output, then upload again.');
66
+ err.code = 'SITE_DENY_PATH';
67
+ err.violations = violations;
68
+ throw err;
69
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@descix/cli",
3
- "version": "1.0.12",
3
+ "version": "1.0.13",
4
4
  "description": "DeSciX CLI and MCP Server - Unified command-line interface for DeSciX platform",
5
5
  "repository": {
6
6
  "type": "git",