@unrdf/kgc-probe 26.4.2
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/README.md +414 -0
- package/package.json +81 -0
- package/src/agents/index.mjs +1402 -0
- package/src/artifact.mjs +405 -0
- package/src/cli.mjs +932 -0
- package/src/config.mjs +115 -0
- package/src/guards.mjs +1213 -0
- package/src/index.mjs +347 -0
- package/src/merge.mjs +196 -0
- package/src/observation.mjs +193 -0
- package/src/orchestrator.mjs +315 -0
- package/src/probe.mjs +58 -0
- package/src/probes/CONCURRENCY-PROBE.md +256 -0
- package/src/probes/README.md +275 -0
- package/src/probes/concurrency.mjs +1175 -0
- package/src/probes/filesystem.mjs +731 -0
- package/src/probes/filesystem.test.mjs +244 -0
- package/src/probes/network.mjs +503 -0
- package/src/probes/performance.mjs +816 -0
- package/src/probes/persistence.mjs +785 -0
- package/src/probes/runtime.mjs +589 -0
- package/src/probes/tooling.mjs +454 -0
- package/src/probes/tooling.test.mjs +372 -0
- package/src/probes/verify-execution.mjs +131 -0
- package/src/probes/verify-guards.mjs +73 -0
- package/src/probes/wasm.mjs +715 -0
- package/src/receipt.mjs +197 -0
- package/src/receipts/index.mjs +813 -0
- package/src/reporter.example.mjs +223 -0
- package/src/reporter.mjs +555 -0
- package/src/reporters/markdown.mjs +355 -0
- package/src/reporters/rdf.mjs +383 -0
- package/src/storage/index.mjs +827 -0
- package/src/types.mjs +1028 -0
- package/src/utils/errors.mjs +397 -0
- package/src/utils/index.mjs +32 -0
- package/src/utils/logger.mjs +236 -0
- package/src/vocabulary.ttl +169 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Filesystem Probe
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
6
|
+
import { probeFilesystem, guardPath } from './filesystem.mjs';
|
|
7
|
+
import fs from 'fs/promises';
|
|
8
|
+
import path from 'path';
|
|
9
|
+
import os from 'os';
|
|
10
|
+
|
|
11
|
+
describe('Filesystem Probe', () => {
|
|
12
|
+
let testRoot;
|
|
13
|
+
let outDir;
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
// Create temp test directory
|
|
17
|
+
testRoot = path.join(os.tmpdir(), `kgc-probe-test-${Date.now()}`);
|
|
18
|
+
outDir = path.join(testRoot, 'out');
|
|
19
|
+
await fs.mkdir(outDir, { recursive: true });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(async () => {
|
|
23
|
+
// Cleanup
|
|
24
|
+
try {
|
|
25
|
+
await fs.rm(testRoot, { recursive: true, force: true });
|
|
26
|
+
} catch {}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('guardPath', () => {
|
|
30
|
+
it('should allow paths within allowed roots', () => {
|
|
31
|
+
const result = guardPath('/home/user/project/file.txt', ['/home/user/project']);
|
|
32
|
+
expect(result.allowed).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should deny paths outside allowed roots', () => {
|
|
36
|
+
const result = guardPath('/etc/passwd', ['/home/user/project']);
|
|
37
|
+
expect(result.allowed).toBe(false);
|
|
38
|
+
expect(result.reason).toContain('outside allowed roots');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('should deny /etc/ paths', () => {
|
|
42
|
+
const result = guardPath('/etc/hosts', ['/etc']);
|
|
43
|
+
expect(result.allowed).toBe(false);
|
|
44
|
+
expect(result.reason).toContain('forbidden pattern');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should deny /root/ paths', () => {
|
|
48
|
+
const result = guardPath('/root/secret.txt', ['/root']);
|
|
49
|
+
expect(result.allowed).toBe(false);
|
|
50
|
+
expect(result.reason).toContain('forbidden pattern');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should deny .ssh directories', () => {
|
|
54
|
+
const result = guardPath('/home/user/.ssh/id_rsa', ['/home/user']);
|
|
55
|
+
expect(result.allowed).toBe(false);
|
|
56
|
+
expect(result.reason).toContain('forbidden pattern');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('should deny .env files', () => {
|
|
60
|
+
const result = guardPath('/home/user/project/.env', ['/home/user/project']);
|
|
61
|
+
expect(result.allowed).toBe(false);
|
|
62
|
+
expect(result.reason).toContain('forbidden pattern');
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it('should deny credentials.json', () => {
|
|
66
|
+
const result = guardPath('/home/user/credentials.json', ['/home/user']);
|
|
67
|
+
expect(result.allowed).toBe(false);
|
|
68
|
+
expect(result.reason).toContain('forbidden pattern');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should deny .pem files', () => {
|
|
72
|
+
const result = guardPath('/home/user/cert.pem', ['/home/user']);
|
|
73
|
+
expect(result.allowed).toBe(false);
|
|
74
|
+
expect(result.reason).toContain('forbidden pattern');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('probeFilesystem', () => {
|
|
79
|
+
it('should return observations array', async () => {
|
|
80
|
+
const observations = await probeFilesystem({
|
|
81
|
+
roots: [testRoot],
|
|
82
|
+
out: outDir,
|
|
83
|
+
budgetMs: 5000
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
expect(Array.isArray(observations)).toBe(true);
|
|
87
|
+
expect(observations.length).toBeGreaterThan(0);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should have valid observation structure', async () => {
|
|
91
|
+
const observations = await probeFilesystem({
|
|
92
|
+
roots: [testRoot],
|
|
93
|
+
out: outDir,
|
|
94
|
+
budgetMs: 5000
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const obs = observations[0];
|
|
98
|
+
expect(obs).toHaveProperty('method');
|
|
99
|
+
expect(obs).toHaveProperty('inputs');
|
|
100
|
+
expect(obs).toHaveProperty('timestamp');
|
|
101
|
+
expect(obs).toHaveProperty('hash');
|
|
102
|
+
expect(obs).toHaveProperty('guardDecision');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should deny access to forbidden paths', async () => {
|
|
106
|
+
const observations = await probeFilesystem({
|
|
107
|
+
roots: ['/etc'],
|
|
108
|
+
out: '/etc/kgc-probe',
|
|
109
|
+
budgetMs: 5000
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const deniedObs = observations.find(o => o.guardDecision === 'denied');
|
|
113
|
+
expect(deniedObs).toBeDefined();
|
|
114
|
+
expect(deniedObs.guardReason).toContain('forbidden pattern');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should deny when output directory outside roots', async () => {
|
|
118
|
+
const observations = await probeFilesystem({
|
|
119
|
+
roots: [testRoot],
|
|
120
|
+
out: '/tmp/outside-roots',
|
|
121
|
+
budgetMs: 5000
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const mainObs = observations.find(o => o.method === 'probeFilesystem');
|
|
125
|
+
if (mainObs) {
|
|
126
|
+
expect(mainObs.guardDecision).toBe('denied');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('should not include outputs for denied operations', async () => {
|
|
131
|
+
const observations = await probeFilesystem({
|
|
132
|
+
roots: ['/etc'],
|
|
133
|
+
out: '/etc/test',
|
|
134
|
+
budgetMs: 5000
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const deniedObs = observations.find(o => o.guardDecision === 'denied');
|
|
138
|
+
if (deniedObs) {
|
|
139
|
+
expect(deniedObs.outputs).toBeUndefined();
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('should complete within budget', async () => {
|
|
144
|
+
const start = Date.now();
|
|
145
|
+
await probeFilesystem({
|
|
146
|
+
roots: [testRoot],
|
|
147
|
+
out: outDir,
|
|
148
|
+
budgetMs: 3000
|
|
149
|
+
});
|
|
150
|
+
const elapsed = Date.now() - start;
|
|
151
|
+
|
|
152
|
+
expect(elapsed).toBeLessThan(4000); // Allow some margin
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
it('should probe read capability', async () => {
|
|
156
|
+
const observations = await probeFilesystem({
|
|
157
|
+
roots: [testRoot],
|
|
158
|
+
out: outDir,
|
|
159
|
+
budgetMs: 5000
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const readObs = observations.find(o => o.method === 'fs.access(R_OK)');
|
|
163
|
+
expect(readObs).toBeDefined();
|
|
164
|
+
expect(readObs.guardDecision).toBe('allowed');
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
it('should probe write capability', async () => {
|
|
168
|
+
const observations = await probeFilesystem({
|
|
169
|
+
roots: [testRoot],
|
|
170
|
+
out: outDir,
|
|
171
|
+
budgetMs: 5000
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const writeObs = observations.find(o => o.method === 'fs.access(W_OK)');
|
|
175
|
+
expect(writeObs).toBeDefined();
|
|
176
|
+
expect(writeObs.guardDecision).toBe('allowed');
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('should probe symlink behavior', async () => {
|
|
180
|
+
const observations = await probeFilesystem({
|
|
181
|
+
roots: [testRoot],
|
|
182
|
+
out: outDir,
|
|
183
|
+
budgetMs: 5000
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const symlinkObs = observations.find(o => o.method === 'fs.symlink');
|
|
187
|
+
expect(symlinkObs).toBeDefined();
|
|
188
|
+
expect(symlinkObs.guardDecision).toBe('allowed');
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
it('should probe directory traversal', async () => {
|
|
192
|
+
const observations = await probeFilesystem({
|
|
193
|
+
roots: [testRoot],
|
|
194
|
+
out: outDir,
|
|
195
|
+
budgetMs: 5000
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
const traversalObs = observations.find(o => o.method === 'fs.readdir(recursive)');
|
|
199
|
+
expect(traversalObs).toBeDefined();
|
|
200
|
+
expect(traversalObs.guardDecision).toBe('allowed');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should have deterministic hashes', async () => {
|
|
204
|
+
const obs1 = await probeFilesystem({
|
|
205
|
+
roots: [testRoot],
|
|
206
|
+
out: outDir,
|
|
207
|
+
budgetMs: 5000
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const obs2 = await probeFilesystem({
|
|
211
|
+
roots: [testRoot],
|
|
212
|
+
out: outDir,
|
|
213
|
+
budgetMs: 5000
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
// Methods should be same
|
|
217
|
+
expect(obs1.map(o => o.method).sort()).toEqual(obs2.map(o => o.method).sort());
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
describe('Observation Schema Compliance', () => {
|
|
222
|
+
it('should include all required fields', async () => {
|
|
223
|
+
const observations = await probeFilesystem({
|
|
224
|
+
roots: [testRoot],
|
|
225
|
+
out: outDir,
|
|
226
|
+
budgetMs: 5000
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
for (const obs of observations) {
|
|
230
|
+
expect(obs.method).toBeDefined();
|
|
231
|
+
expect(obs.inputs).toBeDefined();
|
|
232
|
+
expect(obs.timestamp).toBeDefined();
|
|
233
|
+
expect(obs.hash).toBeDefined();
|
|
234
|
+
expect(obs.guardDecision).toBeDefined();
|
|
235
|
+
|
|
236
|
+
// Validate timestamp format
|
|
237
|
+
expect(() => new Date(obs.timestamp)).not.toThrow();
|
|
238
|
+
|
|
239
|
+
// Validate hash format (SHA256 = 64 hex chars)
|
|
240
|
+
expect(obs.hash).toMatch(/^[a-f0-9]{64}$/);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
});
|
|
244
|
+
});
|