@skillsmith/core 0.5.3 → 0.5.4
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 +10 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/src/activation/ActivationManager.d.ts +7 -0
- package/dist/src/activation/ActivationManager.d.ts.map +1 -1
- package/dist/src/activation/ActivationManager.js +13 -4
- package/dist/src/activation/ActivationManager.js.map +1 -1
- package/dist/src/analysis/adapters/python.d.ts +16 -11
- package/dist/src/analysis/adapters/python.d.ts.map +1 -1
- package/dist/src/analysis/adapters/python.js +46 -61
- package/dist/src/analysis/adapters/python.js.map +1 -1
- package/dist/src/analysis/router.test.d.ts +2 -0
- package/dist/src/analysis/router.test.d.ts.map +1 -0
- package/dist/src/analysis/router.test.js +411 -0
- package/dist/src/analysis/router.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/manager.d.ts.map +1 -1
- package/dist/src/analysis/tree-sitter/manager.js +12 -5
- package/dist/src/analysis/tree-sitter/manager.js.map +1 -1
- package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts +45 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.js +264 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts +12 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.js +74 -0
- package/dist/src/analysis/tree-sitter/pythonExtractor.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts +93 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts +22 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js +229 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.hardening.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.js +287 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.js.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts +17 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.js +142 -0
- package/dist/src/analysis/tree-sitter/pythonIncremental.test.js.map +1 -0
- package/dist/src/analysis/tree-sitter/queries/python.d.ts +43 -0
- package/dist/src/analysis/tree-sitter/queries/python.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/queries/python.js +88 -0
- package/dist/src/analysis/tree-sitter/queries/python.js.map +1 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts +13 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.d.ts.map +1 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js +174 -0
- package/dist/src/analysis/tree-sitter/queryExtractionMatchesOrExceedsRegex.test.js.map +1 -0
- package/dist/src/analytics/ROIDashboardService.csv.d.ts +11 -0
- package/dist/src/analytics/ROIDashboardService.csv.d.ts.map +1 -0
- package/dist/src/analytics/ROIDashboardService.csv.js +43 -0
- package/dist/src/analytics/ROIDashboardService.csv.js.map +1 -0
- package/dist/src/analytics/ROIDashboardService.d.ts +64 -3
- package/dist/src/analytics/ROIDashboardService.d.ts.map +1 -1
- package/dist/src/analytics/ROIDashboardService.js +116 -45
- package/dist/src/analytics/ROIDashboardService.js.map +1 -1
- package/dist/src/api/schemas.d.ts +70 -319
- package/dist/src/api/schemas.d.ts.map +1 -1
- package/dist/src/benchmarks/incrementalParseBenchmark.d.ts +18 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.d.ts.map +1 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.js +121 -0
- package/dist/src/benchmarks/incrementalParseBenchmark.js.map +1 -0
- package/dist/src/billing/GDPRComplianceService.test.d.ts +2 -0
- package/dist/src/billing/GDPRComplianceService.test.d.ts.map +1 -0
- package/dist/src/billing/GDPRComplianceService.test.js +405 -0
- package/dist/src/billing/GDPRComplianceService.test.js.map +1 -0
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/indexer/SkillParser.coverage.test.d.ts +10 -0
- package/dist/src/indexer/SkillParser.coverage.test.d.ts.map +1 -0
- package/dist/src/indexer/SkillParser.coverage.test.js +76 -0
- package/dist/src/indexer/SkillParser.coverage.test.js.map +1 -0
- package/dist/src/indexer/SkillParser.test.d.ts +2 -0
- package/dist/src/indexer/SkillParser.test.d.ts.map +1 -0
- package/dist/src/indexer/SkillParser.test.js +375 -0
- package/dist/src/indexer/SkillParser.test.js.map +1 -0
- package/dist/src/scripts/validation/types.d.ts +14 -24
- package/dist/src/scripts/validation/types.d.ts.map +1 -1
- package/dist/src/services/skill-config-schema.d.ts +4 -36
- package/dist/src/services/skill-config-schema.d.ts.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.d.ts +104 -10
- package/dist/src/sources/LocalFilesystemAdapter.d.ts.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts +92 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.d.ts.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.js +157 -0
- package/dist/src/sources/LocalFilesystemAdapter.helpers.js.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.js +218 -159
- package/dist/src/sources/LocalFilesystemAdapter.js.map +1 -1
- package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts +78 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.d.ts.map +1 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.js +118 -0
- package/dist/src/sources/LocalFilesystemAdapter.scan.js.map +1 -0
- package/dist/src/sources/index.d.ts +1 -1
- package/dist/src/sources/index.d.ts.map +1 -1
- package/dist/src/sources/index.js.map +1 -1
- package/dist/src/sources/types.d.ts +28 -0
- package/dist/src/sources/types.d.ts.map +1 -1
- package/dist/src/telemetry/tracer-imports.d.ts +13 -0
- package/dist/src/telemetry/tracer-imports.d.ts.map +1 -0
- package/dist/src/telemetry/tracer-imports.js +26 -0
- package/dist/src/telemetry/tracer-imports.js.map +1 -0
- package/dist/src/telemetry/tracer.d.ts.map +1 -1
- package/dist/src/telemetry/tracer.js +18 -21
- package/dist/src/telemetry/tracer.js.map +1 -1
- package/dist/src/utils/rate-limit.d.ts +39 -0
- package/dist/src/utils/rate-limit.d.ts.map +1 -0
- package/dist/src/utils/rate-limit.js +48 -0
- package/dist/src/utils/rate-limit.js.map +1 -0
- package/dist/src/utils/rate-limit.test.d.ts +11 -0
- package/dist/src/utils/rate-limit.test.d.ts.map +1 -0
- package/dist/src/utils/rate-limit.test.js +86 -0
- package/dist/src/utils/rate-limit.test.js.map +1 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts +178 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.d.ts.map +1 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.js +196 -0
- package/dist/src/webhooks/WebhookDeadLetterRepository.js.map +1 -0
- package/dist/src/webhooks/WebhookQueue.d.ts +1 -0
- package/dist/src/webhooks/WebhookQueue.d.ts.map +1 -1
- package/dist/src/webhooks/WebhookQueue.js +19 -0
- package/dist/src/webhooks/WebhookQueue.js.map +1 -1
- package/dist/src/webhooks/WebhookQueue.types.d.ts +11 -0
- package/dist/src/webhooks/WebhookQueue.types.d.ts.map +1 -1
- package/dist/src/webhooks/index.d.ts +1 -0
- package/dist/src/webhooks/index.d.ts.map +1 -1
- package/dist/src/webhooks/index.js +2 -0
- package/dist/src/webhooks/index.js.map +1 -1
- package/dist/src/webhooks/webhook-schemas.d.ts +117 -1212
- package/dist/src/webhooks/webhook-schemas.d.ts.map +1 -1
- package/dist/tests/ActivationManager.test.d.ts +13 -0
- package/dist/tests/ActivationManager.test.d.ts.map +1 -0
- package/dist/tests/ActivationManager.test.js +218 -0
- package/dist/tests/ActivationManager.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts +13 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.js +314 -0
- package/dist/tests/LocalFilesystemAdapter.coverage.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.d.ts +18 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.js +344 -0
- package/dist/tests/LocalFilesystemAdapter.security.test.js.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.test.d.ts +12 -0
- package/dist/tests/LocalFilesystemAdapter.test.d.ts.map +1 -0
- package/dist/tests/LocalFilesystemAdapter.test.js +301 -0
- package/dist/tests/LocalFilesystemAdapter.test.js.map +1 -0
- package/dist/tests/ROIDashboardService.coverage.test.d.ts +9 -0
- package/dist/tests/ROIDashboardService.coverage.test.d.ts.map +1 -0
- package/dist/tests/ROIDashboardService.coverage.test.js +118 -0
- package/dist/tests/ROIDashboardService.coverage.test.js.map +1 -0
- package/dist/tests/ROIDashboardService.test.js +87 -0
- package/dist/tests/ROIDashboardService.test.js.map +1 -1
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts +14 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.d.ts.map +1 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.js +169 -0
- package/dist/tests/ScraperAdapters.gitlab-coverage.test.js.map +1 -0
- package/dist/tests/ScraperAdapters.test.d.ts +5 -1
- package/dist/tests/ScraperAdapters.test.d.ts.map +1 -1
- package/dist/tests/ScraperAdapters.test.js +6 -336
- package/dist/tests/ScraperAdapters.test.js.map +1 -1
- package/dist/tests/WebhookDeadLetterRepository.test.d.ts +2 -0
- package/dist/tests/WebhookDeadLetterRepository.test.d.ts.map +1 -0
- package/dist/tests/WebhookDeadLetterRepository.test.js +333 -0
- package/dist/tests/WebhookDeadLetterRepository.test.js.map +1 -0
- package/dist/tests/WebhookHandler.test.js +93 -1
- package/dist/tests/WebhookHandler.test.js.map +1 -1
- package/dist/tests/WebhookQueue.coverage.test.d.ts +19 -0
- package/dist/tests/WebhookQueue.coverage.test.d.ts.map +1 -0
- package/dist/tests/WebhookQueue.coverage.test.js +190 -0
- package/dist/tests/WebhookQueue.coverage.test.js.map +1 -0
- package/dist/tests/billing/GDPRCompliance.test.d.ts +2 -2
- package/dist/tests/billing/GDPRCompliance.test.js +221 -36
- package/dist/tests/billing/GDPRCompliance.test.js.map +1 -1
- package/dist/tests/telemetry.test.js +126 -0
- package/dist/tests/telemetry.test.js.map +1 -1
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts +10 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.d.ts.map +1 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js +109 -0
- package/dist/tests/webhooks/WebhookDeadLetterRepository.test.js.map +1 -0
- package/package.json +8 -3
|
@@ -1,8 +1,36 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Local Filesystem Source Adapter (SMI-591)
|
|
2
|
+
* Local Filesystem Source Adapter (SMI-591, SMI-4287, SMI-4319, SMI-4320)
|
|
3
3
|
*
|
|
4
4
|
* Scans local directories for SKILL.md files.
|
|
5
5
|
* Useful for local development and testing.
|
|
6
|
+
*
|
|
7
|
+
* SMI-4287 hardening:
|
|
8
|
+
* - Symlink targets are resolved via `fs.realpath` and checked against the
|
|
9
|
+
* adapter's `rootDir`. Targets outside root are skipped with a
|
|
10
|
+
* `symlink-escape` warning (unless `allowSymlinksOutsideRoot` is `true`).
|
|
11
|
+
* - Permission (EACCES/EPERM), not-found (ENOENT), and loop (ELOOP) errors
|
|
12
|
+
* are surfaced as `AdapterError` entries on `SourceSearchResult.warnings`
|
|
13
|
+
* instead of throwing, so siblings continue to be scanned.
|
|
14
|
+
* - All `fs.*` calls route through the typed `safeFs` helpers; the historic
|
|
15
|
+
* bare `try/catch` for EACCES in `scanDirectory` is removed.
|
|
16
|
+
*
|
|
17
|
+
* SMI-4319 hardening:
|
|
18
|
+
* - `runScan` allocates a fresh `visitedRealpaths: Set<string>` per
|
|
19
|
+
* invocation so mutually-recursive / self-looping directory symlinks are
|
|
20
|
+
* detected and skipped with a `loop` warning instead of silently wasting
|
|
21
|
+
* `maxDepth` traversals.
|
|
22
|
+
*
|
|
23
|
+
* SMI-4320 hardening:
|
|
24
|
+
* - `resolveSkillPath` is now async and routes through `resolveSafeRealpath`
|
|
25
|
+
* (byte-wise `startsWith(rootReal + sep)` on realpath outputs — no
|
|
26
|
+
* platform lowercasing). Direct-access methods (`getRepository`,
|
|
27
|
+
* `fetchSkillContent`, `skillExists`) inherit containment instead of
|
|
28
|
+
* relying solely on lexical `validatePath`. This closes the scan-to-fetch
|
|
29
|
+
* TOCTOU window where an indexed-then-swapped symlink previously escaped
|
|
30
|
+
* containment. `allowSymlinksOutsideRoot` is honoured at every realpath
|
|
31
|
+
* callsite. Residual TOCTOU between `resolveSkillPath` and the subsequent
|
|
32
|
+
* `fs.readFile` is documented; closing it requires fd-based I/O and is
|
|
33
|
+
* tracked as a separate follow-up.
|
|
6
34
|
*/
|
|
7
35
|
import { BaseSourceAdapter } from './BaseSourceAdapter.js';
|
|
8
36
|
import type { SourceConfig, SourceLocation, SourceRepository, SourceSearchOptions, SourceSearchResult, SkillContent, SourceHealth } from './types.js';
|
|
@@ -18,6 +46,22 @@ export interface LocalFilesystemConfig extends SourceConfig {
|
|
|
18
46
|
excludePatterns?: string[];
|
|
19
47
|
/** Whether to follow symlinks (default: false) */
|
|
20
48
|
followSymlinks?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Allow symlinks whose target resolves outside `rootDir` (SMI-4287).
|
|
51
|
+
*
|
|
52
|
+
* Default `false`: symlinks pointing outside the scan root are skipped and
|
|
53
|
+
* a `symlink-escape` entry is added to `SourceSearchResult.warnings`. This
|
|
54
|
+
* prevents an attacker with write access to `rootDir` from exfiltrating
|
|
55
|
+
* content from arbitrary locations on the filesystem (GitHub #600).
|
|
56
|
+
*
|
|
57
|
+
* Set to `true` only if you trust every symlink inside `rootDir` (e.g.
|
|
58
|
+
* monorepo layouts that intentionally point at sibling packages). The
|
|
59
|
+
* caller accepts the security tradeoff.
|
|
60
|
+
*
|
|
61
|
+
* Note: this flag has no effect when `followSymlinks` is `false` — symlinks
|
|
62
|
+
* are never traversed in that case.
|
|
63
|
+
*/
|
|
64
|
+
allowSymlinksOutsideRoot?: boolean;
|
|
21
65
|
}
|
|
22
66
|
/**
|
|
23
67
|
* Local Filesystem Source Adapter
|
|
@@ -37,6 +81,9 @@ export interface LocalFilesystemConfig extends SourceConfig {
|
|
|
37
81
|
*
|
|
38
82
|
* await adapter.initialize()
|
|
39
83
|
* const result = await adapter.search({})
|
|
84
|
+
* for (const warning of result.warnings ?? []) {
|
|
85
|
+
* console.warn(`[${warning.code}] ${warning.message}`)
|
|
86
|
+
* }
|
|
40
87
|
* ```
|
|
41
88
|
*/
|
|
42
89
|
export declare class LocalFilesystemAdapter extends BaseSourceAdapter {
|
|
@@ -44,26 +91,47 @@ export declare class LocalFilesystemAdapter extends BaseSourceAdapter {
|
|
|
44
91
|
private readonly maxDepth;
|
|
45
92
|
private readonly excludePatterns;
|
|
46
93
|
private readonly followSymlinks;
|
|
94
|
+
private readonly allowSymlinksOutsideRoot;
|
|
47
95
|
private discoveredSkills;
|
|
96
|
+
/**
|
|
97
|
+
* Warnings accumulated during the most recent scan. Consumed and cleared
|
|
98
|
+
* by `search()` so each caller sees only the warnings from that call's
|
|
99
|
+
* underlying scan.
|
|
100
|
+
*/
|
|
101
|
+
private scanWarnings;
|
|
48
102
|
constructor(config: LocalFilesystemConfig);
|
|
49
103
|
/**
|
|
50
104
|
* Initialize by scanning the filesystem
|
|
51
105
|
*/
|
|
52
106
|
protected doInitialize(): Promise<void>;
|
|
53
107
|
/**
|
|
54
|
-
* Check if root directory exists and is accessible
|
|
108
|
+
* Check if root directory exists and is accessible.
|
|
109
|
+
*
|
|
110
|
+
* SMI-4287: routes `fs.stat(rootDir)` through `safeFs` so the raw Node
|
|
111
|
+
* error is translated to a typed `AdapterError` message.
|
|
55
112
|
*/
|
|
56
113
|
protected doHealthCheck(): Promise<Partial<SourceHealth>>;
|
|
57
114
|
/**
|
|
58
|
-
* Search for skills in the scanned directories
|
|
115
|
+
* Search for skills in the scanned directories.
|
|
116
|
+
*
|
|
117
|
+
* SMI-4287: `warnings` collects non-fatal `AdapterError` entries from the
|
|
118
|
+
* scan (symlink escapes, permission denials, loops). An empty array is
|
|
119
|
+
* returned as `undefined` to keep the field strictly optional.
|
|
59
120
|
*/
|
|
60
121
|
search(options?: SourceSearchOptions): Promise<SourceSearchResult>;
|
|
61
122
|
/**
|
|
62
|
-
* Get repository info for a skill location
|
|
123
|
+
* Get repository info for a skill location.
|
|
124
|
+
*
|
|
125
|
+
* SMI-4287: `fs.stat` is routed through `safeFs`; permission errors are
|
|
126
|
+
* converted to typed Error messages instead of raw Node throws.
|
|
63
127
|
*/
|
|
64
128
|
getRepository(location: SourceLocation): Promise<SourceRepository>;
|
|
65
129
|
/**
|
|
66
|
-
* Fetch skill content from local file
|
|
130
|
+
* Fetch skill content from local file.
|
|
131
|
+
*
|
|
132
|
+
* SMI-4287: both `fs.readFile` and `fs.stat` route through `safeFs`, so
|
|
133
|
+
* permission errors (EACCES/EPERM) raise typed Errors with path context
|
|
134
|
+
* instead of raw Node errors.
|
|
67
135
|
*/
|
|
68
136
|
fetchSkillContent(location: SourceLocation): Promise<SkillContent>;
|
|
69
137
|
/**
|
|
@@ -71,7 +139,10 @@ export declare class LocalFilesystemAdapter extends BaseSourceAdapter {
|
|
|
71
139
|
*/
|
|
72
140
|
skillExists(location: SourceLocation): Promise<boolean>;
|
|
73
141
|
/**
|
|
74
|
-
* Rescan the filesystem for new skills
|
|
142
|
+
* Rescan the filesystem for new skills.
|
|
143
|
+
*
|
|
144
|
+
* Returns the count of discovered skills. Warnings from the rescan are
|
|
145
|
+
* available via the next call to `search()`.
|
|
75
146
|
*/
|
|
76
147
|
rescan(): Promise<number>;
|
|
77
148
|
/**
|
|
@@ -79,9 +150,16 @@ export declare class LocalFilesystemAdapter extends BaseSourceAdapter {
|
|
|
79
150
|
*/
|
|
80
151
|
get skillCount(): number;
|
|
81
152
|
/**
|
|
82
|
-
*
|
|
153
|
+
* Run the recursive scan starting at `rootDir`. Delegates to the extracted
|
|
154
|
+
* `scanDirectoryRecursive` helper (see `LocalFilesystemAdapter.scan.ts`).
|
|
155
|
+
*
|
|
156
|
+
* SMI-4319: allocates a fresh `visitedRealpaths` set per invocation so
|
|
157
|
+
* back-to-back scans don't share state. Sibling directories within a
|
|
158
|
+
* single scan share the set (they're in the same call tree), so
|
|
159
|
+
* cross-linked loops (A↔B) are caught even when the loop isn't on the
|
|
160
|
+
* descent path from `rootDir`.
|
|
83
161
|
*/
|
|
84
|
-
private
|
|
162
|
+
private runScan;
|
|
85
163
|
/**
|
|
86
164
|
* Check if a path/name should be excluded (SMI-722, SMI-726)
|
|
87
165
|
* Uses centralized safe pattern matching to prevent RegExp injection
|
|
@@ -89,11 +167,27 @@ export declare class LocalFilesystemAdapter extends BaseSourceAdapter {
|
|
|
89
167
|
private isExcluded;
|
|
90
168
|
/**
|
|
91
169
|
* Resolve a skill location to a full filesystem path
|
|
92
|
-
*
|
|
170
|
+
* (SMI-720, SMI-726, SMI-4287, SMI-4320).
|
|
171
|
+
*
|
|
172
|
+
* Two-stage containment: (1) lexical `validatePath` fast-fails
|
|
173
|
+
* `../`-style traversal (SMI-720 contract — callers assert the
|
|
174
|
+
* "Path traversal detected" message), then (2) `resolveSafeRealpath`
|
|
175
|
+
* enforces realpath byte-wise containment so symlinks can't escape
|
|
176
|
+
* `rootDir` even when the lexical path is clean. Honours the SMI-4287
|
|
177
|
+
* `allowSymlinksOutsideRoot` opt-in.
|
|
178
|
+
*
|
|
179
|
+
* Not-found behaviour: realpath ENOENT falls back to the lexically
|
|
180
|
+
* resolved path so downstream `stat` / `readFile` produce the canonical
|
|
181
|
+
* caller-visible error. TOCTOU caveat: the window between this resolve
|
|
182
|
+
* and the caller's subsequent read remains open; closing it requires
|
|
183
|
+
* fd-based I/O and is tracked separately.
|
|
93
184
|
*/
|
|
94
185
|
private resolveSkillPath;
|
|
95
186
|
/**
|
|
96
|
-
* Convert discovered skill to SourceRepository
|
|
187
|
+
* Convert discovered skill to SourceRepository.
|
|
188
|
+
*
|
|
189
|
+
* Returns both the repository and any warnings encountered while reading
|
|
190
|
+
* the file (typically permission errors on SKILL.md after discovery).
|
|
97
191
|
*/
|
|
98
192
|
private skillToRepository;
|
|
99
193
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalFilesystemAdapter.d.ts","sourceRoot":"","sources":["../../../src/sources/LocalFilesystemAdapter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"LocalFilesystemAdapter.d.ts","sourceRoot":"","sources":["../../../src/sources/LocalFilesystemAdapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,KAAK,EACV,YAAY,EACZ,cAAc,EACd,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,YAAY,EACZ,YAAY,EAEb,MAAM,YAAY,CAAA;AAanB;;GAEG;AACH,MAAM,WAAW,qBAAsB,SAAQ,YAAY;IACzD,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAA;IACf,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,uCAAuC;IACvC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;IAC1B,kDAAkD;IAClD,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;;;;;;;;;;;;;;OAcG;IACH,wBAAwB,CAAC,EAAE,OAAO,CAAA;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,qBAAa,sBAAuB,SAAQ,iBAAiB;IAC3D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAQ;IACjC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAS;IAClD,OAAO,CAAC,gBAAgB,CAA8B;IACtD;;;;OAIG;IACH,OAAO,CAAC,YAAY,CAAqB;gBAE7B,MAAM,EAAE,qBAAqB;IASzC;;OAEG;cACsB,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAKtD;;;;;OAKG;cACa,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;IAc/D;;;;;;OAMG;IACG,MAAM,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAmC5E;;;;;OAKG;IACG,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAkCxE;;;;;;OAMG;IACG,iBAAiB,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC;IAyBxE;;OAEG;IACY,WAAW,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IAUtE;;;;;OAKG;IACG,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC;IAO/B;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED;;;;;;;;;OASG;YACW,OAAO;IAcrB;;;OAGG;IACH,OAAO,CAAC,UAAU;IAIlB;;;;;;;;;;;;;;;;OAgBG;YACW,gBAAgB;IA2C9B;;;;;OAKG;YACW,iBAAiB;IAiD/B;;OAEG;IACH,OAAO,CAAC,UAAU;IAIlB;;OAEG;IACH,OAAO,CAAC,WAAW;CAGpB;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,qBAAqB,GAC5B,sBAAsB,CAMxB"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalFilesystemAdapter helpers (SMI-4287, SMI-4319, SMI-4320)
|
|
3
|
+
*
|
|
4
|
+
* Typed filesystem wrappers and symlink containment helpers used by
|
|
5
|
+
* LocalFilesystemAdapter. These surface `AdapterError` return values instead
|
|
6
|
+
* of throwing, so the adapter can continue scanning past individual failures
|
|
7
|
+
* (permission, symlink escape, loop) and aggregate them into
|
|
8
|
+
* `SourceSearchResult.warnings`.
|
|
9
|
+
*
|
|
10
|
+
* SMI-4320: drops platform-based `normaliseForFs` in favour of byte-wise
|
|
11
|
+
* `startsWith(root + sep)` on realpath outputs. The FS itself canonicalises
|
|
12
|
+
* case via `realpath` — platform heuristics miscategorise case-sensitive
|
|
13
|
+
* volumes (HFS+ case-sensitive macOS volumes, ext4 case-folded dirs).
|
|
14
|
+
* `resolveSafeRealpath` now accepts an `allowSymlinksOutsideRoot` opt-in
|
|
15
|
+
* (see SMI-4287) so direct-access callers can inherit the same containment
|
|
16
|
+
* policy as the scan loop.
|
|
17
|
+
*/
|
|
18
|
+
import { type Dirent, type Stats } from 'fs';
|
|
19
|
+
import type { AdapterError } from './types.js';
|
|
20
|
+
/**
|
|
21
|
+
* Success / failure result envelope used by `safeFs` helpers.
|
|
22
|
+
*
|
|
23
|
+
* @typeParam T - Type of the successful value
|
|
24
|
+
*/
|
|
25
|
+
export type FsResult<T> = {
|
|
26
|
+
ok: true;
|
|
27
|
+
value: T;
|
|
28
|
+
} | {
|
|
29
|
+
ok: false;
|
|
30
|
+
error: AdapterError;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* Safe filesystem wrappers that return `FsResult<T>` instead of throwing.
|
|
34
|
+
*/
|
|
35
|
+
export declare const safeFs: {
|
|
36
|
+
readonly readdir: (path: string) => Promise<FsResult<Dirent[]>>;
|
|
37
|
+
readonly stat: (path: string) => Promise<FsResult<Stats>>;
|
|
38
|
+
readonly readFile: (path: string, encoding?: BufferEncoding) => Promise<FsResult<string>>;
|
|
39
|
+
readonly realpath: (path: string) => Promise<FsResult<string>>;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Byte-wise containment check on two realpath outputs (SMI-4320).
|
|
43
|
+
*
|
|
44
|
+
* Compares raw realpath bytes: `candidateReal === rootReal` or
|
|
45
|
+
* `candidateReal.startsWith(rootReal + sep)`. No platform lowercasing — the
|
|
46
|
+
* filesystem is authoritative. Case-insensitive volumes (APFS default, NTFS)
|
|
47
|
+
* already canonicalise case through `fs.realpath`; case-sensitive volumes
|
|
48
|
+
* (HFS+ case-sensitive, ext4 case-folded) keep distinct paths distinct.
|
|
49
|
+
*
|
|
50
|
+
* The trailing-separator guard is load-bearing: without `+ sep`,
|
|
51
|
+
* `rootDir = /a/root` would accept `/a/rootfoo` as contained.
|
|
52
|
+
*/
|
|
53
|
+
export declare function isRealpathContained(candidateReal: string, rootReal: string): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Options accepted by `resolveSafeRealpath`.
|
|
56
|
+
*
|
|
57
|
+
* - `allowSymlinksOutsideRoot` (SMI-4287): when `true`, skip the containment
|
|
58
|
+
* re-check and return the realpath unconditionally. Callers that opt in
|
|
59
|
+
* accept the security tradeoff — used by dev-install tooling that scans
|
|
60
|
+
* linked sibling packages.
|
|
61
|
+
*/
|
|
62
|
+
export interface ResolveSafeRealpathOptions {
|
|
63
|
+
allowSymlinksOutsideRoot?: boolean;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Resolve `candidate` to a realpath and verify it remains within `root`.
|
|
67
|
+
*
|
|
68
|
+
* Runs `fs.realpath` on both the candidate and the root, then performs a
|
|
69
|
+
* byte-wise `startsWith(rootReal + sep)` check (SMI-4320). On case-insensitive
|
|
70
|
+
* volumes the FS canonicalises case inside `realpath`; on case-sensitive
|
|
71
|
+
* volumes distinct cases remain distinct — both outcomes are correct.
|
|
72
|
+
*
|
|
73
|
+
* Returns `{ ok: true, value: resolvedRealpath }` on success. On failure
|
|
74
|
+
* returns an `AdapterError` with:
|
|
75
|
+
* - `symlink-escape` if the target resolves outside the root
|
|
76
|
+
* - `loop` on `ELOOP` (circular symlinks)
|
|
77
|
+
* - `permission` / `not-found` / `io` for other filesystem errors
|
|
78
|
+
*
|
|
79
|
+
* Does NOT throw for containment violations (caller drives the warning list).
|
|
80
|
+
*
|
|
81
|
+
* SMI-4287 opt-in: when `opts.allowSymlinksOutsideRoot === true`, containment
|
|
82
|
+
* is skipped entirely. The loop-detection + other realpath errors still apply.
|
|
83
|
+
*
|
|
84
|
+
* TOCTOU caveat: this is a check-then-use pattern. Between the realpath
|
|
85
|
+
* check and a subsequent `fs.readFile`, a malicious actor with write access
|
|
86
|
+
* inside `rootDir` could swap the symlink target. True atomicity requires
|
|
87
|
+
* fd-based I/O (`fs.open` + fstat-by-fd + read-by-fd) and is out of scope
|
|
88
|
+
* here — this helper closes the 99% case where the attack window is
|
|
89
|
+
* scan-to-fetch (minutes to hours). See plan doc for the residual risk.
|
|
90
|
+
*/
|
|
91
|
+
export declare function resolveSafeRealpath(candidate: string, root: string, opts?: ResolveSafeRealpathOptions): Promise<FsResult<string>>;
|
|
92
|
+
//# sourceMappingURL=LocalFilesystemAdapter.helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalFilesystemAdapter.helpers.d.ts","sourceRoot":"","sources":["../../../src/sources/LocalFilesystemAdapter.helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAkB,KAAK,MAAM,EAAE,KAAK,KAAK,EAAE,MAAM,IAAI,CAAA;AAE5D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C;;;;GAIG;AACH,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,CAAC,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,YAAY,CAAA;CAAE,CAAA;AA0DrF;;GAEG;AACH,eAAO,MAAM,MAAM;6BACH,MAAM,KAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;0BAGvC,MAAM,KAAG,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;8BAG7B,MAAM,aAAY,cAAc,KAAa,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;8BAGtE,MAAM,KAAG,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;CAGzC,CAAA;AAEV;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAEpF;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA0B;IACzC,wBAAwB,CAAC,EAAE,OAAO,CAAA;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAsB,mBAAmB,CACvC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,0BAA+B,GACpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CA0B3B"}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalFilesystemAdapter helpers (SMI-4287, SMI-4319, SMI-4320)
|
|
3
|
+
*
|
|
4
|
+
* Typed filesystem wrappers and symlink containment helpers used by
|
|
5
|
+
* LocalFilesystemAdapter. These surface `AdapterError` return values instead
|
|
6
|
+
* of throwing, so the adapter can continue scanning past individual failures
|
|
7
|
+
* (permission, symlink escape, loop) and aggregate them into
|
|
8
|
+
* `SourceSearchResult.warnings`.
|
|
9
|
+
*
|
|
10
|
+
* SMI-4320: drops platform-based `normaliseForFs` in favour of byte-wise
|
|
11
|
+
* `startsWith(root + sep)` on realpath outputs. The FS itself canonicalises
|
|
12
|
+
* case via `realpath` — platform heuristics miscategorise case-sensitive
|
|
13
|
+
* volumes (HFS+ case-sensitive macOS volumes, ext4 case-folded dirs).
|
|
14
|
+
* `resolveSafeRealpath` now accepts an `allowSymlinksOutsideRoot` opt-in
|
|
15
|
+
* (see SMI-4287) so direct-access callers can inherit the same containment
|
|
16
|
+
* policy as the scan loop.
|
|
17
|
+
*/
|
|
18
|
+
import { promises as fs } from 'fs';
|
|
19
|
+
import { sep } from 'path';
|
|
20
|
+
/**
|
|
21
|
+
* Map a Node filesystem error to an `AdapterError.code`.
|
|
22
|
+
*/
|
|
23
|
+
function mapErrnoToCode(err) {
|
|
24
|
+
const code = err?.code;
|
|
25
|
+
switch (code) {
|
|
26
|
+
case 'EACCES':
|
|
27
|
+
case 'EPERM':
|
|
28
|
+
return 'permission';
|
|
29
|
+
case 'ENOENT':
|
|
30
|
+
return 'not-found';
|
|
31
|
+
case 'ELOOP':
|
|
32
|
+
return 'loop';
|
|
33
|
+
default:
|
|
34
|
+
return 'io';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Build a human-friendly message for an AdapterError.
|
|
39
|
+
*/
|
|
40
|
+
function describe(code, path) {
|
|
41
|
+
switch (code) {
|
|
42
|
+
case 'permission':
|
|
43
|
+
return `Cannot read: ${path}`;
|
|
44
|
+
case 'not-found':
|
|
45
|
+
return `Not found: ${path}`;
|
|
46
|
+
case 'loop':
|
|
47
|
+
return `Symlink loop detected: ${path}`;
|
|
48
|
+
case 'symlink-escape':
|
|
49
|
+
return `Symlink outside root, skipped: ${path}`;
|
|
50
|
+
case 'io':
|
|
51
|
+
return `Filesystem error: ${path}`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Wrap a throwing `fs` call into an `FsResult<T>`.
|
|
56
|
+
*/
|
|
57
|
+
async function wrap(path, op) {
|
|
58
|
+
try {
|
|
59
|
+
return { ok: true, value: await op() };
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
const code = mapErrnoToCode(error);
|
|
63
|
+
return {
|
|
64
|
+
ok: false,
|
|
65
|
+
error: {
|
|
66
|
+
code,
|
|
67
|
+
path,
|
|
68
|
+
message: describe(code, path),
|
|
69
|
+
cause: error,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Safe filesystem wrappers that return `FsResult<T>` instead of throwing.
|
|
76
|
+
*/
|
|
77
|
+
export const safeFs = {
|
|
78
|
+
readdir(path) {
|
|
79
|
+
return wrap(path, () => fs.readdir(path, { withFileTypes: true }));
|
|
80
|
+
},
|
|
81
|
+
stat(path) {
|
|
82
|
+
return wrap(path, () => fs.stat(path));
|
|
83
|
+
},
|
|
84
|
+
readFile(path, encoding = 'utf-8') {
|
|
85
|
+
return wrap(path, () => fs.readFile(path, encoding));
|
|
86
|
+
},
|
|
87
|
+
realpath(path) {
|
|
88
|
+
return wrap(path, () => fs.realpath(path));
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Byte-wise containment check on two realpath outputs (SMI-4320).
|
|
93
|
+
*
|
|
94
|
+
* Compares raw realpath bytes: `candidateReal === rootReal` or
|
|
95
|
+
* `candidateReal.startsWith(rootReal + sep)`. No platform lowercasing — the
|
|
96
|
+
* filesystem is authoritative. Case-insensitive volumes (APFS default, NTFS)
|
|
97
|
+
* already canonicalise case through `fs.realpath`; case-sensitive volumes
|
|
98
|
+
* (HFS+ case-sensitive, ext4 case-folded) keep distinct paths distinct.
|
|
99
|
+
*
|
|
100
|
+
* The trailing-separator guard is load-bearing: without `+ sep`,
|
|
101
|
+
* `rootDir = /a/root` would accept `/a/rootfoo` as contained.
|
|
102
|
+
*/
|
|
103
|
+
export function isRealpathContained(candidateReal, rootReal) {
|
|
104
|
+
return candidateReal === rootReal || candidateReal.startsWith(rootReal + sep);
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Resolve `candidate` to a realpath and verify it remains within `root`.
|
|
108
|
+
*
|
|
109
|
+
* Runs `fs.realpath` on both the candidate and the root, then performs a
|
|
110
|
+
* byte-wise `startsWith(rootReal + sep)` check (SMI-4320). On case-insensitive
|
|
111
|
+
* volumes the FS canonicalises case inside `realpath`; on case-sensitive
|
|
112
|
+
* volumes distinct cases remain distinct — both outcomes are correct.
|
|
113
|
+
*
|
|
114
|
+
* Returns `{ ok: true, value: resolvedRealpath }` on success. On failure
|
|
115
|
+
* returns an `AdapterError` with:
|
|
116
|
+
* - `symlink-escape` if the target resolves outside the root
|
|
117
|
+
* - `loop` on `ELOOP` (circular symlinks)
|
|
118
|
+
* - `permission` / `not-found` / `io` for other filesystem errors
|
|
119
|
+
*
|
|
120
|
+
* Does NOT throw for containment violations (caller drives the warning list).
|
|
121
|
+
*
|
|
122
|
+
* SMI-4287 opt-in: when `opts.allowSymlinksOutsideRoot === true`, containment
|
|
123
|
+
* is skipped entirely. The loop-detection + other realpath errors still apply.
|
|
124
|
+
*
|
|
125
|
+
* TOCTOU caveat: this is a check-then-use pattern. Between the realpath
|
|
126
|
+
* check and a subsequent `fs.readFile`, a malicious actor with write access
|
|
127
|
+
* inside `rootDir` could swap the symlink target. True atomicity requires
|
|
128
|
+
* fd-based I/O (`fs.open` + fstat-by-fd + read-by-fd) and is out of scope
|
|
129
|
+
* here — this helper closes the 99% case where the attack window is
|
|
130
|
+
* scan-to-fetch (minutes to hours). See plan doc for the residual risk.
|
|
131
|
+
*/
|
|
132
|
+
export async function resolveSafeRealpath(candidate, root, opts = {}) {
|
|
133
|
+
const candidateResult = await safeFs.realpath(candidate);
|
|
134
|
+
if (!candidateResult.ok)
|
|
135
|
+
return candidateResult;
|
|
136
|
+
if (opts.allowSymlinksOutsideRoot === true) {
|
|
137
|
+
return candidateResult;
|
|
138
|
+
}
|
|
139
|
+
const rootResult = await safeFs.realpath(root);
|
|
140
|
+
if (!rootResult.ok)
|
|
141
|
+
return rootResult;
|
|
142
|
+
if (!isRealpathContained(candidateResult.value, rootResult.value)) {
|
|
143
|
+
// Report the symlink path the user can identify (not the opaque
|
|
144
|
+
// realpath target). Including the target would leak the external
|
|
145
|
+
// location, which is exactly what the guard is protecting against.
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
error: {
|
|
149
|
+
code: 'symlink-escape',
|
|
150
|
+
path: candidate,
|
|
151
|
+
message: describe('symlink-escape', candidate),
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return { ok: true, value: candidateResult.value };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=LocalFilesystemAdapter.helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalFilesystemAdapter.helpers.js","sourceRoot":"","sources":["../../../src/sources/LocalFilesystemAdapter.helpers.ts"],"names":[],"mappings":"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"}
|