axiodb 7.33.233 → 7.33.234

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 (30) hide show
  1. package/.agents/skills/axiodb-development/SKILL.md +284 -0
  2. package/.gemini/settings.json +53 -0
  3. package/lib/Helper/DocumentLoader.helper.d.ts +45 -0
  4. package/lib/Helper/DocumentLoader.helper.js +91 -0
  5. package/lib/Helper/DocumentLoader.helper.js.map +1 -0
  6. package/lib/Helper/PathSanitizer.helper.d.ts +69 -0
  7. package/lib/Helper/PathSanitizer.helper.js +113 -0
  8. package/lib/Helper/PathSanitizer.helper.js.map +1 -0
  9. package/lib/Services/Aggregation/Aggregation.Operation.js +8 -20
  10. package/lib/Services/Aggregation/Aggregation.Operation.js.map +1 -1
  11. package/lib/Services/CRUD Operation/Create.operation.js +7 -2
  12. package/lib/Services/CRUD Operation/Create.operation.js.map +1 -1
  13. package/lib/Services/CRUD Operation/Delete.operation.d.ts +1 -0
  14. package/lib/Services/CRUD Operation/Delete.operation.js +11 -62
  15. package/lib/Services/CRUD Operation/Delete.operation.js.map +1 -1
  16. package/lib/Services/CRUD Operation/Reader.operation.js +13 -29
  17. package/lib/Services/CRUD Operation/Reader.operation.js.map +1 -1
  18. package/lib/Services/CRUD Operation/Update.operation.d.ts +1 -0
  19. package/lib/Services/CRUD Operation/Update.operation.js +11 -62
  20. package/lib/Services/CRUD Operation/Update.operation.js.map +1 -1
  21. package/lib/Services/Database/database.operation.js +5 -2
  22. package/lib/Services/Database/database.operation.js.map +1 -1
  23. package/lib/engine/Filesystem/FileManager.d.ts +13 -0
  24. package/lib/engine/Filesystem/FileManager.js +49 -0
  25. package/lib/engine/Filesystem/FileManager.js.map +1 -1
  26. package/package.json +1 -1
  27. package/.antigravity/rules.md +0 -228
  28. package/.cursorrules +0 -60
  29. package/AI_TOOLS_SETUP.md +0 -368
  30. /package/.cursor/rules/{axiodb-core.md → axiodb-core.mdc} +0 -0
@@ -0,0 +1,284 @@
1
+ ---
2
+ name: axiodb-development
3
+ description: Core development rules and patterns for AxioDB embedded NoSQL database
4
+ version: 1.0.0
5
+ tags: [typescript, database, testing, build]
6
+ author: AxioDB Team
7
+ ---
8
+
9
+ # AxioDB Development Skill
10
+
11
+ ## Project Identity
12
+
13
+ **AxioDB** - Embedded NoSQL database for Node.js
14
+ - TypeScript strict mode → CommonJS
15
+ - Node.js ≥20.0.0
16
+ - Zero native dependencies
17
+ - Singleton pattern, file-per-document storage
18
+
19
+ ## Mandatory Workflows
20
+
21
+ ### After EVERY Code Change
22
+ ```bash
23
+ npm run build # MANDATORY - catch TypeScript errors immediately
24
+ ```
25
+
26
+ ### For ANY Feature Change
27
+ 1. Update tests in `Test/modules/`
28
+ 2. Run `npm test` - all tests must pass
29
+ 3. Update docs (README.md, Document/, Dockerfile)
30
+ 4. Run `npm run lint` - must pass
31
+
32
+ ## Definition of "Done"
33
+
34
+ A task is NOT complete until ALL of these are true:
35
+ - ✅ Code written following standards
36
+ - ✅ `npm run build` passes with zero errors
37
+ - ✅ Tests added/updated in `Test/modules/`
38
+ - ✅ `npm test` passes - all tests green
39
+ - ✅ `npm run lint` passes
40
+ - ✅ Documentation updated (README, Document/, Dockerfile)
41
+ - ✅ No breaking changes (or explicitly noted and approved)
42
+ - ✅ Self-reviewed for performance, security, maintainability
43
+
44
+ ## Architecture Constraints
45
+
46
+ ### Singleton Pattern (NON-NEGOTIABLE)
47
+ ```typescript
48
+ export class AxioDB {
49
+ private static _instance: AxioDB;
50
+
51
+ constructor() {
52
+ if (AxioDB._instance) {
53
+ throw new Error("Only one instance of AxioDB is allowed.");
54
+ }
55
+ AxioDB._instance = this;
56
+ }
57
+ }
58
+ ```
59
+
60
+ **Critical Implication**: Tests MUST run in separate child processes. Cannot run tests in parallel.
61
+
62
+ ### File-Per-Document Storage
63
+ ```
64
+ {RootPath}/{DatabaseName}/{CollectionName}/{documentId}.axiodb
65
+ ```
66
+
67
+ ### Dual-Write Pattern (Indexes)
68
+ ```typescript
69
+ // ALWAYS write to BOTH memory AND disk
70
+ await this.indexCache.set(key, data, TTL); // Memory (speed)
71
+ await this.fileManager.writeFile(path, JSON.stringify(data)); // Disk (durability)
72
+ ```
73
+
74
+ ### Random Cache TTL (5-15 minutes)
75
+ ```typescript
76
+ const TTL = Math.floor(Math.random() * (15 - 5 + 1) + 5) * 60 * 1000;
77
+ ```
78
+ **Why**: Prevents cache stampede/thundering herd
79
+
80
+ ## TypeScript Standards (STRICT)
81
+
82
+ ### NO `any` Types - EVER
83
+ ```typescript
84
+ // ❌ ABSOLUTELY FORBIDDEN
85
+ const result: any = await operation();
86
+
87
+ // ✅ REQUIRED
88
+ interface OperationResult {
89
+ success: boolean;
90
+ data: DocumentData;
91
+ }
92
+ const result: OperationResult = await operation();
93
+ ```
94
+
95
+ ### Strict Null Checks
96
+ ```typescript
97
+ // ✅ GOOD
98
+ function get(id: string): Document | null {
99
+ return this.cache.get(id) ?? null;
100
+ }
101
+
102
+ const doc = get('123');
103
+ if (doc !== null) {
104
+ console.log(doc.name);
105
+ }
106
+ ```
107
+
108
+ ## SOLID Principles
109
+
110
+ ### Single Responsibility
111
+ Each class/module has ONE reason to change.
112
+
113
+ ### Don't Repeat Yourself (DRY)
114
+ If same logic appears in 2+ files, extract to `Helper/` directory.
115
+
116
+ ### No Hacky Solutions
117
+ ```typescript
118
+ // ❌ FORBIDDEN
119
+ setTimeout(() => { /* hope this works */ }, 1000);
120
+ eval(userInput);
121
+ try { risky(); } catch (e) { /* ignore */ }
122
+
123
+ // ✅ REQUIRED
124
+ await properAsyncOperation();
125
+ const sanitized = validateAndSanitize(userInput);
126
+ try { await risky(); } catch (error) {
127
+ logger.error('Operation failed', error);
128
+ return ResponseHelper.error('Failed', StatusCodes.ERROR);
129
+ }
130
+ ```
131
+
132
+ ## Testing Requirements
133
+
134
+ ### Location
135
+ ```
136
+ Test/modules/
137
+ ├── crud.test.js # CRUD operations
138
+ ├── transaction.test.js # Transactions, WAL, savepoints
139
+ └── read.test.js # Read optimizations, caching
140
+ ```
141
+
142
+ ### Coverage Required
143
+ - Happy path (success scenarios)
144
+ - Edge cases (empty, null, undefined, large data)
145
+ - Error cases (validation failures, file errors, conflicts)
146
+ - Concurrent operations
147
+ - Backward compatibility
148
+
149
+ ## Module Organization
150
+
151
+ ```
152
+ source/
153
+ ├── Services/
154
+ │ ├── Database/ # Database ops
155
+ │ ├── Collection/ # Collection ops
156
+ │ ├── CRUD Operation/ # Create, Reader, Update, Delete
157
+ │ ├── Index/ # Index management (cache + disk)
158
+ │ ├── Aggregation/ # MongoDB-style pipelines
159
+ │ └── Transaction/ # ACID, WAL, sessions
160
+ ├── engine/Filesystem/ # FileManager, FolderManager
161
+ ├── server/ # HTTP GUI (Fastify, port 27018)
162
+ ├── tcp/ # TCP server (AxioDBCloud, port 27019)
163
+ ├── client/ # AxioDBCloud TCP client
164
+ ├── Helper/ # Converter, Crypto, Response
165
+ └── Memory/ # InMemoryCache
166
+ ```
167
+
168
+ ## Naming Conventions
169
+
170
+ - **Files**: `{Feature}.operation.ts`, `{Feature}.service.ts`, `{Feature}.helper.ts`
171
+ - **Classes**: PascalCase: `FileManager`, `QueryMatcher`
172
+ - **Methods**: camelCase: `createDatabase()`, `isValidDocument()`
173
+ - **Variables**: camelCase: `documentId`, `collectionPath`
174
+
175
+ ## Performance Standards
176
+
177
+ ### 1. Use InMemoryCache
178
+ ```typescript
179
+ const cached = this.cache.get(key);
180
+ if (cached) return cached;
181
+
182
+ const data = await this.readFromDisk(id);
183
+ this.cache.set(key, data, TTL);
184
+ return data;
185
+ ```
186
+
187
+ ### 2. Batch Operations
188
+ ```typescript
189
+ // ✅ PARALLEL
190
+ await Promise.all(docs.map(d => this.insert(d)));
191
+
192
+ // ❌ SEQUENTIAL (slow)
193
+ for (const d of docs) { await this.insert(d); }
194
+ ```
195
+
196
+ ### 3. Use Map for O(1) Lookups
197
+ ```typescript
198
+ // ✅ O(1)
199
+ const map = new Map<string, Document>();
200
+ const found = map.get(id);
201
+
202
+ // ❌ O(n)
203
+ const found = array.find(d => d.id === id);
204
+ ```
205
+
206
+ ## Security Standards
207
+
208
+ ### 1. Validate All Input
209
+ ```typescript
210
+ if (!document || typeof document !== 'object') {
211
+ return this.ResponseHelper.error('Invalid', StatusCodes.BAD_REQUEST);
212
+ }
213
+ if (Array.isArray(document)) {
214
+ return this.ResponseHelper.error('Cannot be array', StatusCodes.BAD_REQUEST);
215
+ }
216
+ ```
217
+
218
+ ### 2. Sanitize File Paths
219
+ ```typescript
220
+ // Prevent path traversal
221
+ const sanitized = documentId.replace(/[^a-zA-Z0-9-_]/g, '_');
222
+ return path.join(collectionPath, `${sanitized}.axiodb`);
223
+ ```
224
+
225
+ ### 3. Handle Sensitive Data
226
+ ```typescript
227
+ // Encrypt collections with sensitive data
228
+ const users = await db.createCollection('Users', true, process.env.KEY);
229
+
230
+ // Never log passwords
231
+ logger.info('Auth', { userId }); // ✅
232
+ logger.info('Auth', { password }); // ❌
233
+ ```
234
+
235
+ ## Documentation Requirements
236
+
237
+ Update when features change:
238
+
239
+ 1. **README.md** - Public API, features, quick start examples
240
+ 2. **Document/** - React docs site (`cd Document && npm run dev`)
241
+ 3. **Dockerfile** - If ports, env vars, or commands change
242
+ 4. **JSDoc** - All public methods with examples
243
+
244
+ ## Anti-Patterns (FORBIDDEN)
245
+
246
+ ❌ Using `any` types
247
+ ❌ Duplicated code (violates DRY)
248
+ ❌ Sequential operations that could be parallel
249
+ ❌ Ignoring build errors
250
+ ❌ Skipping tests
251
+ ❌ Missing documentation
252
+ ❌ Hardcoded values
253
+ ❌ Magic strings/numbers
254
+ ❌ Deep nesting (>3 levels)
255
+ ❌ Unclear variable names
256
+ ❌ Suppressing errors without handling
257
+
258
+ ## Commands
259
+
260
+ ```bash
261
+ # Build & Test
262
+ npm run build # TypeScript → lib/ (MANDATORY)
263
+ npm test # All tests
264
+ npm test crud # CRUD tests only
265
+ npm test transaction # Transaction tests only
266
+ npm test read # Read optimization tests
267
+ npm run lint # ESLint
268
+
269
+ # Development
270
+ node Test/modules/crud.test.js # Run specific test
271
+ cd Document && npm run dev # Docs site (localhost:5173)
272
+ ```
273
+
274
+ ## Success Criteria
275
+
276
+ Every task must meet ALL:
277
+ - ✅ Builds successfully (`npm run build`)
278
+ - ✅ Tests pass (`npm test`)
279
+ - ✅ Lint passes (`npm run lint`)
280
+ - ✅ Docs updated
281
+ - ✅ No regressions
282
+ - ✅ Follows patterns
283
+ - ✅ Security validated
284
+ - ✅ Performance acceptable
@@ -0,0 +1,53 @@
1
+ {
2
+ "version": "1.0",
3
+ "description": "NoSQL database management system for efficient data storage and retrieval.",
4
+ "project": {
5
+ "name": "AxioDB",
6
+ "type": "NoSQL Database",
7
+ "language": "TypeScript",
8
+ "runtime": "Node.js ≥18.0.0"
9
+ },
10
+ "context": {
11
+ "fileName": [
12
+ "GEMINI.md"
13
+ ],
14
+ "hierarchical": true,
15
+ "scanSubdirectories": true
16
+ },
17
+ "tools": {
18
+ "enabled": [
19
+ "codeSearch",
20
+ "fileOperations",
21
+ "shellCommands"
22
+ ],
23
+ "codeSearch": {
24
+ "indexFiles": true,
25
+ "excludePatterns": [
26
+ "node_modules",
27
+ "lib",
28
+ "dist",
29
+ ".git"
30
+ ]
31
+ }
32
+ },
33
+ "safety": {
34
+ "blockNone": false,
35
+ "blockFewUnsafe": true,
36
+ "blockSomeUnsafe": true,
37
+ "blockMostUnsafe": true
38
+ },
39
+ "performance": {
40
+ "streamResponse": true,
41
+ "cacheContext": true,
42
+ "maxCachedContexts": 10
43
+ },
44
+ "features": {
45
+ "codeCompletion": true,
46
+ "codeGeneration": true,
47
+ "codeExplanation": true,
48
+ "debugging": true,
49
+ "refactoring": true,
50
+ "testing": true,
51
+ "documentation": true
52
+ }
53
+ }
@@ -0,0 +1,45 @@
1
+ import { SuccessInterface, ErrorInterface } from '../config/Interfaces/Helper/response.helper.interface';
2
+ /**
3
+ * DocumentLoader - Shared utility for loading documents from collection directories
4
+ *
5
+ * Provides a centralized method for loading document files using worker threads,
6
+ * replacing duplicated code in Reader, Update, Delete, and Aggregation operations.
7
+ *
8
+ * @class DocumentLoader
9
+ */
10
+ export default class DocumentLoader {
11
+ private static readonly ResponseHelper;
12
+ /**
13
+ * Loads all documents from a collection directory using worker threads
14
+ *
15
+ * This method consolidates the LoadAllBufferRawData logic that was previously
16
+ * duplicated across multiple CRUD operations. It handles both direct file
17
+ * specification and directory scanning with .axiodb file filtering.
18
+ *
19
+ * @param collectionPath - Full path to collection directory
20
+ * @param encryptionKey - Optional encryption key for encrypted documents
21
+ * @param isEncrypted - Whether documents are encrypted (default: false)
22
+ * @param documentFiles - Optional specific file names to load
23
+ * @param includeFileName - Whether to include fileName in result (default: false)
24
+ * @returns Success with document array or Error
25
+ *
26
+ * @example
27
+ * // Load all documents from a collection
28
+ * const result = await DocumentLoader.loadDocuments(
29
+ * '/path/to/collection',
30
+ * undefined,
31
+ * false
32
+ * );
33
+ *
34
+ * @example
35
+ * // Load specific documents with filenames included
36
+ * const result = await DocumentLoader.loadDocuments(
37
+ * '/path/to/collection',
38
+ * 'encryption-key',
39
+ * true,
40
+ * ['doc1.axiodb', 'doc2.axiodb'],
41
+ * true
42
+ * );
43
+ */
44
+ static loadDocuments(collectionPath: string, encryptionKey?: string, isEncrypted?: boolean, documentFiles?: string[], includeFileName?: boolean): Promise<SuccessInterface | ErrorInterface>;
45
+ }
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ const FolderManager_1 = __importDefault(require("../engine/Filesystem/FolderManager"));
16
+ const BufferLoaderWithWorker_utils_1 = __importDefault(require("../utility/BufferLoaderWithWorker.utils"));
17
+ const response_helper_1 = __importDefault(require("./response.helper"));
18
+ /**
19
+ * DocumentLoader - Shared utility for loading documents from collection directories
20
+ *
21
+ * Provides a centralized method for loading document files using worker threads,
22
+ * replacing duplicated code in Reader, Update, Delete, and Aggregation operations.
23
+ *
24
+ * @class DocumentLoader
25
+ */
26
+ class DocumentLoader {
27
+ /**
28
+ * Loads all documents from a collection directory using worker threads
29
+ *
30
+ * This method consolidates the LoadAllBufferRawData logic that was previously
31
+ * duplicated across multiple CRUD operations. It handles both direct file
32
+ * specification and directory scanning with .axiodb file filtering.
33
+ *
34
+ * @param collectionPath - Full path to collection directory
35
+ * @param encryptionKey - Optional encryption key for encrypted documents
36
+ * @param isEncrypted - Whether documents are encrypted (default: false)
37
+ * @param documentFiles - Optional specific file names to load
38
+ * @param includeFileName - Whether to include fileName in result (default: false)
39
+ * @returns Success with document array or Error
40
+ *
41
+ * @example
42
+ * // Load all documents from a collection
43
+ * const result = await DocumentLoader.loadDocuments(
44
+ * '/path/to/collection',
45
+ * undefined,
46
+ * false
47
+ * );
48
+ *
49
+ * @example
50
+ * // Load specific documents with filenames included
51
+ * const result = await DocumentLoader.loadDocuments(
52
+ * '/path/to/collection',
53
+ * 'encryption-key',
54
+ * true,
55
+ * ['doc1.axiodb', 'doc2.axiodb'],
56
+ * true
57
+ * );
58
+ */
59
+ static loadDocuments(collectionPath_1, encryptionKey_1) {
60
+ return __awaiter(this, arguments, void 0, function* (collectionPath, encryptionKey, isEncrypted = false, documentFiles, includeFileName = false) {
61
+ try {
62
+ const dataFilesList = [];
63
+ if (documentFiles !== undefined) {
64
+ // Use provided file list
65
+ dataFilesList.push(...documentFiles);
66
+ }
67
+ else {
68
+ // Scan directory for .axiodb files
69
+ const readResponse = yield new FolderManager_1.default().ListDirectory(collectionPath);
70
+ if ("data" in readResponse) {
71
+ // Filter for .axiodb files only
72
+ const axiodbFiles = readResponse.data.filter((file) => file.endsWith(".axiodb"));
73
+ dataFilesList.push(...axiodbFiles);
74
+ }
75
+ else {
76
+ return this.ResponseHelper.Error("Failed to read directory");
77
+ }
78
+ }
79
+ // Load all files using worker threads (parallel processing)
80
+ const resultData = yield (0, BufferLoaderWithWorker_utils_1.default)(dataFilesList, encryptionKey, collectionPath, isEncrypted, includeFileName);
81
+ return this.ResponseHelper.Success(resultData);
82
+ }
83
+ catch (error) {
84
+ return this.ResponseHelper.Error(error);
85
+ }
86
+ });
87
+ }
88
+ }
89
+ DocumentLoader.ResponseHelper = new response_helper_1.default();
90
+ exports.default = DocumentLoader;
91
+ //# sourceMappingURL=DocumentLoader.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DocumentLoader.helper.js","sourceRoot":"","sources":["../../source/Helper/DocumentLoader.helper.ts"],"names":[],"mappings":";;;;;;;;;;;;;;AACA,uFAA+D;AAC/D,2GAAuE;AACvE,wEAA+C;AAE/C;;;;;;;GAOG;AACH,MAAqB,cAAc;IAGjC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,MAAM,CAAO,aAAa;6DACxB,cAAsB,EACtB,aAAsB,EACtB,cAAuB,KAAK,EAC5B,aAAwB,EACxB,kBAA2B,KAAK;YAEhC,IAAI,CAAC;gBACH,MAAM,aAAa,GAAa,EAAE,CAAC;gBAEnC,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;oBAChC,yBAAyB;oBACzB,aAAa,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;gBACvC,CAAC;qBAAM,CAAC;oBACN,mCAAmC;oBACnC,MAAM,YAAY,GAAG,MAAM,IAAI,uBAAa,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;oBAE7E,IAAI,MAAM,IAAI,YAAY,EAAE,CAAC;wBAC3B,gCAAgC;wBAChC,MAAM,WAAW,GAAG,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAC5D,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,CACzB,CAAC;wBACF,aAAa,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;oBACrC,CAAC;yBAAM,CAAC;wBACN,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC;oBAC/D,CAAC;gBACH,CAAC;gBAED,4DAA4D;gBAC5D,MAAM,UAAU,GAAG,MAAM,IAAA,sCAAgB,EACvC,aAAa,EACb,aAAa,EACb,cAAc,EACd,WAAW,EACX,eAAe,CAChB,CAAC;gBAEF,OAAO,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YACjD,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;KAAA;;AA3EuB,6BAAc,GAAG,IAAI,yBAAc,EAAE,CAAC;kBAD3C,cAAc"}
@@ -0,0 +1,69 @@
1
+ /**
2
+ * PathSanitizer - Security helper to prevent directory traversal attacks
3
+ *
4
+ * Provides methods to sanitize user-controlled path components and validate
5
+ * that constructed paths remain within expected boundaries.
6
+ *
7
+ * @class PathSanitizer
8
+ * @example
9
+ * const safe = PathSanitizer.sanitizePathComponent('../../etc/passwd');
10
+ * // Returns: '______etc_passwd'
11
+ */
12
+ export default class PathSanitizer {
13
+ /**
14
+ * Sanitizes user input to prevent directory traversal attacks
15
+ *
16
+ * Removes dangerous characters that could be used for path traversal,
17
+ * including: '../', '/', '\', null bytes, and other special characters.
18
+ *
19
+ * @param userInput - Potentially malicious path component
20
+ * @returns Sanitized string safe for file paths
21
+ * @throws Error if input is invalid or results in empty string
22
+ *
23
+ * @example
24
+ * PathSanitizer.sanitizePathComponent('../../../etc/passwd');
25
+ * // Returns: '______etc_passwd'
26
+ *
27
+ * PathSanitizer.sanitizePathComponent('safe-name_123');
28
+ * // Returns: 'safe-name_123'
29
+ */
30
+ static sanitizePathComponent(userInput: string): string;
31
+ /**
32
+ * Validates that resolved path is within basePath (defense in depth)
33
+ *
34
+ * This provides an additional security layer by ensuring the final
35
+ * constructed path hasn't escaped the expected base directory through
36
+ * symlinks or other means.
37
+ *
38
+ * @param basePath - Expected parent directory
39
+ * @param fullPath - Constructed path to validate
40
+ * @throws Error if path traversal detected
41
+ *
42
+ * @example
43
+ * PathSanitizer.validatePath('/app/data', '/app/data/users/123.axiodb');
44
+ * // No error - path is within base
45
+ *
46
+ * PathSanitizer.validatePath('/app/data', '/etc/passwd');
47
+ * // Throws: Security violation: Path traversal attempt detected
48
+ */
49
+ static validatePath(basePath: string, fullPath: string): void;
50
+ /**
51
+ * Safe path join with automatic sanitization and validation
52
+ *
53
+ * Combines base path with one or more path components after sanitizing
54
+ * each component and validating the final path remains within the base.
55
+ *
56
+ * @param basePath - Base directory (trusted path)
57
+ * @param components - Path components to join (will be sanitized)
58
+ * @returns Safe joined path guaranteed to be within basePath
59
+ * @throws Error if sanitization fails or path traversal detected
60
+ *
61
+ * @example
62
+ * PathSanitizer.safePath('/app/data', 'users', 'doc123.axiodb');
63
+ * // Returns: '/app/data/users/doc123.axiodb'
64
+ *
65
+ * PathSanitizer.safePath('/app/data', '../../../etc/passwd');
66
+ * // Throws: Security violation (after sanitization and validation)
67
+ */
68
+ static safePath(basePath: string, ...components: string[]): string;
69
+ }
@@ -0,0 +1,113 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const path_1 = __importDefault(require("path"));
7
+ /**
8
+ * PathSanitizer - Security helper to prevent directory traversal attacks
9
+ *
10
+ * Provides methods to sanitize user-controlled path components and validate
11
+ * that constructed paths remain within expected boundaries.
12
+ *
13
+ * @class PathSanitizer
14
+ * @example
15
+ * const safe = PathSanitizer.sanitizePathComponent('../../etc/passwd');
16
+ * // Returns: '______etc_passwd'
17
+ */
18
+ class PathSanitizer {
19
+ /**
20
+ * Sanitizes user input to prevent directory traversal attacks
21
+ *
22
+ * Removes dangerous characters that could be used for path traversal,
23
+ * including: '../', '/', '\', null bytes, and other special characters.
24
+ *
25
+ * @param userInput - Potentially malicious path component
26
+ * @returns Sanitized string safe for file paths
27
+ * @throws Error if input is invalid or results in empty string
28
+ *
29
+ * @example
30
+ * PathSanitizer.sanitizePathComponent('../../../etc/passwd');
31
+ * // Returns: '______etc_passwd'
32
+ *
33
+ * PathSanitizer.sanitizePathComponent('safe-name_123');
34
+ * // Returns: 'safe-name_123'
35
+ */
36
+ static sanitizePathComponent(userInput) {
37
+ if (!userInput || typeof userInput !== 'string') {
38
+ throw new Error('Invalid path component: must be a non-empty string');
39
+ }
40
+ // Remove all directory traversal attempts
41
+ let sanitized = userInput
42
+ .replace(/\.\./g, '_') // Remove .. (parent directory)
43
+ .replace(/\//g, '_') // Remove / (Unix path separator)
44
+ .replace(/\\/g, '_') // Remove \ (Windows path separator)
45
+ .replace(/\0/g, '_'); // Remove null bytes
46
+ // Allow only alphanumeric, dash, underscore, and dot (for file extensions)
47
+ // Note: We preserve dots for file extensions but removed '..' above
48
+ sanitized = sanitized.replace(/[^a-zA-Z0-9-_.]/g, '_');
49
+ // Prevent empty result
50
+ if (sanitized.length === 0) {
51
+ throw new Error('Invalid path component: results in empty string after sanitization');
52
+ }
53
+ // Prevent starting with dot (hidden files) to avoid potential issues
54
+ if (sanitized.startsWith('.')) {
55
+ sanitized = '_' + sanitized.substring(1);
56
+ }
57
+ return sanitized;
58
+ }
59
+ /**
60
+ * Validates that resolved path is within basePath (defense in depth)
61
+ *
62
+ * This provides an additional security layer by ensuring the final
63
+ * constructed path hasn't escaped the expected base directory through
64
+ * symlinks or other means.
65
+ *
66
+ * @param basePath - Expected parent directory
67
+ * @param fullPath - Constructed path to validate
68
+ * @throws Error if path traversal detected
69
+ *
70
+ * @example
71
+ * PathSanitizer.validatePath('/app/data', '/app/data/users/123.axiodb');
72
+ * // No error - path is within base
73
+ *
74
+ * PathSanitizer.validatePath('/app/data', '/etc/passwd');
75
+ * // Throws: Security violation: Path traversal attempt detected
76
+ */
77
+ static validatePath(basePath, fullPath) {
78
+ const resolvedBase = path_1.default.resolve(basePath);
79
+ const resolvedFull = path_1.default.resolve(fullPath);
80
+ if (!resolvedFull.startsWith(resolvedBase)) {
81
+ throw new Error('Security violation: Path traversal attempt detected');
82
+ }
83
+ }
84
+ /**
85
+ * Safe path join with automatic sanitization and validation
86
+ *
87
+ * Combines base path with one or more path components after sanitizing
88
+ * each component and validating the final path remains within the base.
89
+ *
90
+ * @param basePath - Base directory (trusted path)
91
+ * @param components - Path components to join (will be sanitized)
92
+ * @returns Safe joined path guaranteed to be within basePath
93
+ * @throws Error if sanitization fails or path traversal detected
94
+ *
95
+ * @example
96
+ * PathSanitizer.safePath('/app/data', 'users', 'doc123.axiodb');
97
+ * // Returns: '/app/data/users/doc123.axiodb'
98
+ *
99
+ * PathSanitizer.safePath('/app/data', '../../../etc/passwd');
100
+ * // Throws: Security violation (after sanitization and validation)
101
+ */
102
+ static safePath(basePath, ...components) {
103
+ // Sanitize each component
104
+ const sanitizedComponents = components.map(c => this.sanitizePathComponent(c));
105
+ // Join paths using Node.js path.join (handles platform differences)
106
+ const fullPath = path_1.default.join(basePath, ...sanitizedComponents);
107
+ // Validate final path is within base (defense in depth)
108
+ this.validatePath(basePath, fullPath);
109
+ return fullPath;
110
+ }
111
+ }
112
+ exports.default = PathSanitizer;
113
+ //# sourceMappingURL=PathSanitizer.helper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PathSanitizer.helper.js","sourceRoot":"","sources":["../../source/Helper/PathSanitizer.helper.ts"],"names":[],"mappings":";;;;;AAAA,gDAAwB;AAExB;;;;;;;;;;GAUG;AACH,MAAqB,aAAa;IAChC;;;;;;;;;;;;;;;;OAgBG;IACH,MAAM,CAAC,qBAAqB,CAAC,SAAiB;QAC5C,IAAI,CAAC,SAAS,IAAI,OAAO,SAAS,KAAK,QAAQ,EAAE,CAAC;YAChD,MAAM,IAAI,KAAK,CAAC,oDAAoD,CAAC,CAAC;QACxE,CAAC;QAED,0CAA0C;QAC1C,IAAI,SAAS,GAAG,SAAS;aACtB,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAI,+BAA+B;aACxD,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAM,iCAAiC;aAC1D,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAM,oCAAoC;aAC7D,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,CAAK,oBAAoB;QAEhD,2EAA2E;QAC3E,oEAAoE;QACpE,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC,CAAC;QAEvD,uBAAuB;QACvB,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CAAC,oEAAoE,CAAC,CAAC;QACxF,CAAC;QAED,qEAAqE;QACrE,IAAI,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC9B,SAAS,GAAG,GAAG,GAAG,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC;QAED,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,YAAY,CAAC,QAAgB,EAAE,QAAgB;QACpD,MAAM,YAAY,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC5C,MAAM,YAAY,GAAG,cAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAE5C,IAAI,CAAC,YAAY,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAC3C,MAAM,IAAI,KAAK,CAAC,qDAAqD,CAAC,CAAC;QACzE,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAgB,EAAE,GAAG,UAAoB;QACvD,0BAA0B;QAC1B,MAAM,mBAAmB,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;QAE/E,oEAAoE;QACpE,MAAM,QAAQ,GAAG,cAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,mBAAmB,CAAC,CAAC;QAE7D,wDAAwD;QACxD,IAAI,CAAC,YAAY,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAEtC,OAAO,QAAQ,CAAC;IAClB,CAAC;CACF;AAxGD,gCAwGC"}