@secure-exec/core 0.2.0-rc.1 → 0.2.0-rc.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.
@@ -480,6 +480,11 @@ export const NODE_CUSTOM_GLOBAL_INVENTORY = [
480
480
  classification: "hardened",
481
481
  rationale: "Host HTTP/2 session settings bridge reference.",
482
482
  },
483
+ {
484
+ name: "_networkHttp2SessionSetLocalWindowSizeRaw",
485
+ classification: "hardened",
486
+ rationale: "Host HTTP/2 session local-window bridge reference.",
487
+ },
483
488
  {
484
489
  name: "_networkHttp2SessionGoawayRaw",
485
490
  classification: "hardened",
@@ -500,6 +505,16 @@ export const NODE_CUSTOM_GLOBAL_INVENTORY = [
500
505
  classification: "hardened",
501
506
  rationale: "Host HTTP/2 session lifetime bridge reference.",
502
507
  },
508
+ {
509
+ name: "_networkHttp2ServerPollRaw",
510
+ classification: "hardened",
511
+ rationale: "Host HTTP/2 server event-poll bridge reference.",
512
+ },
513
+ {
514
+ name: "_networkHttp2SessionPollRaw",
515
+ classification: "hardened",
516
+ rationale: "Host HTTP/2 session event-poll bridge reference.",
517
+ },
503
518
  {
504
519
  name: "_networkHttp2StreamRespondRaw",
505
520
  classification: "hardened",
@@ -520,6 +535,31 @@ export const NODE_CUSTOM_GLOBAL_INVENTORY = [
520
535
  classification: "hardened",
521
536
  rationale: "Host HTTP/2 stream end bridge reference.",
522
537
  },
538
+ {
539
+ name: "_networkHttp2StreamCloseRaw",
540
+ classification: "hardened",
541
+ rationale: "Host HTTP/2 stream close bridge reference.",
542
+ },
543
+ {
544
+ name: "_networkHttp2StreamPauseRaw",
545
+ classification: "hardened",
546
+ rationale: "Host HTTP/2 stream pause bridge reference.",
547
+ },
548
+ {
549
+ name: "_networkHttp2StreamResumeRaw",
550
+ classification: "hardened",
551
+ rationale: "Host HTTP/2 stream resume bridge reference.",
552
+ },
553
+ {
554
+ name: "_networkHttp2StreamRespondWithFileRaw",
555
+ classification: "hardened",
556
+ rationale: "Host HTTP/2 stream respondWithFile bridge reference.",
557
+ },
558
+ {
559
+ name: "_networkHttp2ServerRespondRaw",
560
+ classification: "hardened",
561
+ rationale: "Host HTTP/2 server-response bridge reference.",
562
+ },
523
563
  {
524
564
  name: "_upgradeSocketWriteRaw",
525
565
  classification: "hardened",
@@ -610,6 +650,46 @@ export const NODE_CUSTOM_GLOBAL_INVENTORY = [
610
650
  classification: "hardened",
611
651
  rationale: "Host net server close bridge reference.",
612
652
  },
653
+ {
654
+ name: "_dgramSocketCreateRaw",
655
+ classification: "hardened",
656
+ rationale: "Host dgram socket create bridge reference.",
657
+ },
658
+ {
659
+ name: "_dgramSocketBindRaw",
660
+ classification: "hardened",
661
+ rationale: "Host dgram socket bind bridge reference.",
662
+ },
663
+ {
664
+ name: "_dgramSocketRecvRaw",
665
+ classification: "hardened",
666
+ rationale: "Host dgram socket receive bridge reference.",
667
+ },
668
+ {
669
+ name: "_dgramSocketSendRaw",
670
+ classification: "hardened",
671
+ rationale: "Host dgram socket send bridge reference.",
672
+ },
673
+ {
674
+ name: "_dgramSocketCloseRaw",
675
+ classification: "hardened",
676
+ rationale: "Host dgram socket close bridge reference.",
677
+ },
678
+ {
679
+ name: "_dgramSocketAddressRaw",
680
+ classification: "hardened",
681
+ rationale: "Host dgram socket address bridge reference.",
682
+ },
683
+ {
684
+ name: "_dgramSocketSetBufferSizeRaw",
685
+ classification: "hardened",
686
+ rationale: "Host dgram socket buffer-size setter bridge reference.",
687
+ },
688
+ {
689
+ name: "_dgramSocketGetBufferSizeRaw",
690
+ classification: "hardened",
691
+ rationale: "Host dgram socket buffer-size getter bridge reference.",
692
+ },
613
693
  {
614
694
  name: "_batchResolveModules",
615
695
  classification: "hardened",
@@ -715,11 +795,26 @@ export const NODE_CUSTOM_GLOBAL_INVENTORY = [
715
795
  classification: "hardened",
716
796
  rationale: "Network Response API global — must not be replaceable by sandbox code.",
717
797
  },
798
+ {
799
+ name: "DOMException",
800
+ classification: "hardened",
801
+ rationale: "DOMException global stub for undici/bootstrap compatibility.",
802
+ },
803
+ {
804
+ name: "__importMetaResolve",
805
+ classification: "hardened",
806
+ rationale: "Internal import.meta.resolve helper for transformed ESM modules.",
807
+ },
718
808
  {
719
809
  name: "Blob",
720
810
  classification: "hardened",
721
811
  rationale: "Blob API global stub — must not be replaceable by sandbox code.",
722
812
  },
813
+ {
814
+ name: "File",
815
+ classification: "hardened",
816
+ rationale: "File API global stub — must not be replaceable by sandbox code.",
817
+ },
723
818
  {
724
819
  name: "FormData",
725
820
  classification: "hardened",
@@ -53,9 +53,12 @@ export declare class InMemoryFileSystem implements VirtualFileSystem {
53
53
  private cloneInode;
54
54
  private allocateFileInode;
55
55
  private allocateDirectoryInode;
56
+ private allocateSymlinkInode;
56
57
  private updateFileMetadata;
57
58
  private requirePathInode;
58
59
  private requireFileInode;
59
60
  private requireInode;
61
+ private ensureDirectory;
62
+ private adjustParentDirectoryLinkCount;
60
63
  }
61
64
  export declare function createInMemoryFileSystem(): InMemoryFileSystem;
@@ -123,7 +123,7 @@ export class InMemoryFileSystem {
123
123
  });
124
124
  }
125
125
  }
126
- for (const linkPath of this.symlinks.keys()) {
126
+ for (const [linkPath, link] of this.symlinks.entries()) {
127
127
  if (!linkPath.startsWith(prefix))
128
128
  continue;
129
129
  const rest = linkPath.slice(prefix.length);
@@ -132,7 +132,7 @@ export class InMemoryFileSystem {
132
132
  name: rest,
133
133
  isDirectory: false,
134
134
  isSymbolicLink: true,
135
- ino: 0,
135
+ ino: link.ino,
136
136
  });
137
137
  }
138
138
  }
@@ -189,9 +189,7 @@ export class InMemoryFileSystem {
189
189
  let current = "";
190
190
  for (const part of parts) {
191
191
  current += `/${part}`;
192
- if (!this.dirs.has(current)) {
193
- this.dirs.set(current, this.allocateDirectoryInode().ino);
194
- }
192
+ this.ensureDirectory(current);
195
193
  }
196
194
  const inode = this.allocateFileInode();
197
195
  this.files.set(resolved, inode.ino);
@@ -218,18 +216,14 @@ export class InMemoryFileSystem {
218
216
  if (!this.dirs.has(parent)) {
219
217
  throw new Error(`ENOENT: no such file or directory, mkdir '${normalized}'`);
220
218
  }
221
- if (!this.dirs.has(normalized)) {
222
- this.dirs.set(normalized, this.allocateDirectoryInode().ino);
223
- }
219
+ this.ensureDirectory(normalized);
224
220
  }
225
221
  async mkdir(path, _options) {
226
222
  const parts = splitPath(path);
227
223
  let current = "";
228
224
  for (const part of parts) {
229
225
  current += `/${part}`;
230
- if (!this.dirs.has(current)) {
231
- this.dirs.set(current, this.allocateDirectoryInode().ino);
232
- }
226
+ this.ensureDirectory(current);
233
227
  }
234
228
  }
235
229
  resolveIfSymlink(normalized) {
@@ -238,22 +232,23 @@ export class InMemoryFileSystem {
238
232
  resolveSymlink(normalized, maxDepth = 16) {
239
233
  let current = normalized;
240
234
  for (let i = 0; i < maxDepth; i++) {
241
- const target = this.symlinks.get(current);
242
- if (!target)
235
+ const link = this.symlinks.get(current);
236
+ if (!link)
243
237
  return current;
244
- current = target.startsWith("/")
245
- ? normalizePath(target)
246
- : normalizePath(`${dirname(current)}/${target}`);
238
+ current = link.target.startsWith("/")
239
+ ? normalizePath(link.target)
240
+ : normalizePath(`${dirname(current)}/${link.target}`);
247
241
  }
248
242
  throw new Error(`ELOOP: too many levels of symbolic links, stat '${normalized}'`);
249
243
  }
250
244
  statForInode(inode) {
251
245
  const isDirectory = (inode.mode & 0o170000) === S_IFDIR;
246
+ const isSymbolicLink = (inode.mode & 0o170000) === S_IFLNK;
252
247
  return {
253
248
  mode: inode.mode,
254
249
  size: isDirectory ? 4096 : inode.size,
255
250
  isDirectory,
256
- isSymbolicLink: false,
251
+ isSymbolicLink,
257
252
  atimeMs: inode.atime.getTime(),
258
253
  mtimeMs: inode.mtime.getTime(),
259
254
  ctimeMs: inode.ctime.getTime(),
@@ -295,7 +290,13 @@ export class InMemoryFileSystem {
295
290
  }
296
291
  async removeFile(path) {
297
292
  const normalized = normalizePath(path);
298
- if (this.symlinks.delete(normalized)) {
293
+ const symlink = this.symlinks.get(normalized);
294
+ if (symlink) {
295
+ this.symlinks.delete(normalized);
296
+ this.inodeTable.decrementLinks(symlink.ino);
297
+ if (this.inodeTable.shouldDelete(symlink.ino)) {
298
+ this.inodeTable.delete(symlink.ino);
299
+ }
299
300
  return;
300
301
  }
301
302
  const resolved = this.resolveSymlink(normalized);
@@ -337,6 +338,8 @@ export class InMemoryFileSystem {
337
338
  const ino = this.dirs.get(normalized);
338
339
  this.dirs.delete(normalized);
339
340
  this.inodeTable.decrementLinks(ino);
341
+ this.inodeTable.decrementLinks(ino);
342
+ this.adjustParentDirectoryLinkCount(normalized, -1);
340
343
  if (this.inodeTable.shouldDelete(ino)) {
341
344
  this.inodeTable.delete(ino);
342
345
  }
@@ -412,6 +415,10 @@ export class InMemoryFileSystem {
412
415
  for (const [path, target] of symlinkEntries) {
413
416
  this.symlinks.set(`${targetPrefix}${path.slice(sourcePrefix.length)}`, target);
414
417
  }
418
+ if (dirname(oldNormalized) !== dirname(newNormalized)) {
419
+ this.adjustParentDirectoryLinkCount(oldNormalized, -1);
420
+ this.adjustParentDirectoryLinkCount(newNormalized, 1);
421
+ }
415
422
  }
416
423
  async symlink(target, linkPath) {
417
424
  const normalized = normalizePath(linkPath);
@@ -421,35 +428,22 @@ export class InMemoryFileSystem {
421
428
  throw new Error(`EEXIST: file already exists, symlink '${target}' -> '${normalized}'`);
422
429
  }
423
430
  await this.mkdir(dirname(normalized));
424
- this.symlinks.set(normalized, target);
431
+ const inode = this.allocateSymlinkInode(target);
432
+ this.symlinks.set(normalized, { target, ino: inode.ino });
425
433
  }
426
434
  async readlink(path) {
427
435
  const normalized = normalizePath(path);
428
- const target = this.symlinks.get(normalized);
429
- if (target === undefined) {
436
+ const link = this.symlinks.get(normalized);
437
+ if (link === undefined) {
430
438
  throw new Error(`EINVAL: invalid argument, readlink '${normalized}'`);
431
439
  }
432
- return target;
440
+ return link.target;
433
441
  }
434
442
  async lstat(path) {
435
443
  const normalized = normalizePath(path);
436
- const target = this.symlinks.get(normalized);
437
- if (target !== undefined) {
438
- const now = Date.now();
439
- return {
440
- mode: S_IFLNK | 0o777,
441
- size: new TextEncoder().encode(target).byteLength,
442
- isDirectory: false,
443
- isSymbolicLink: true,
444
- atimeMs: now,
445
- mtimeMs: now,
446
- ctimeMs: now,
447
- birthtimeMs: now,
448
- ino: 0,
449
- nlink: 1,
450
- uid: 0,
451
- gid: 0,
452
- };
444
+ const link = this.symlinks.get(normalized);
445
+ if (link !== undefined) {
446
+ return this.statForInode(this.requireInode(link.ino));
453
447
  }
454
448
  return this.statEntry(normalized);
455
449
  }
@@ -532,11 +526,13 @@ export class InMemoryFileSystem {
532
526
  reindexInodes(oldTable) {
533
527
  const oldContents = new Map(this.fileContents);
534
528
  const oldFiles = new Map(this.files);
529
+ const oldSymlinks = new Map(this.symlinks);
535
530
  const oldDirs = Array.from(this.dirs.entries()).sort(([a], [b]) => a.length - b.length);
536
531
  const inoMap = new Map();
537
532
  this.files = new Map();
538
533
  this.fileContents = new Map();
539
534
  this.dirs = new Map();
535
+ this.symlinks = new Map();
540
536
  for (const [dirPath, oldIno] of oldDirs) {
541
537
  const ino = this.cloneInode(oldIno, oldTable, S_IFDIR | 0o755).ino;
542
538
  this.dirs.set(dirPath, ino);
@@ -557,6 +553,11 @@ export class InMemoryFileSystem {
557
553
  this.requireInode(mapped).size = content.byteLength;
558
554
  }
559
555
  }
556
+ for (const [path, link] of oldSymlinks) {
557
+ const mapped = this.cloneInode(link.ino, oldTable, S_IFLNK | 0o777).ino;
558
+ this.symlinks.set(path, { target: link.target, ino: mapped });
559
+ this.requireInode(mapped).size = new TextEncoder().encode(link.target).byteLength;
560
+ }
560
561
  }
561
562
  cloneInode(oldIno, oldTable, fallbackMode) {
562
563
  const source = oldTable.get(oldIno);
@@ -575,9 +576,15 @@ export class InMemoryFileSystem {
575
576
  }
576
577
  allocateDirectoryInode() {
577
578
  const inode = this.inodeTable.allocate(S_IFDIR | 0o755, 0, 0);
579
+ inode.nlink = 2;
578
580
  inode.size = 4096;
579
581
  return inode;
580
582
  }
583
+ allocateSymlinkInode(target) {
584
+ const inode = this.inodeTable.allocate(S_IFLNK | 0o777, 0, 0);
585
+ inode.size = new TextEncoder().encode(target).byteLength;
586
+ return inode;
587
+ }
581
588
  updateFileMetadata(ino, size) {
582
589
  const inode = this.requireFileInode(ino);
583
590
  const now = new Date();
@@ -609,6 +616,34 @@ export class InMemoryFileSystem {
609
616
  }
610
617
  return inode;
611
618
  }
619
+ ensureDirectory(path) {
620
+ const normalized = normalizePath(path);
621
+ if (normalized === "/")
622
+ return;
623
+ if (this.dirs.has(normalized))
624
+ return;
625
+ const parent = dirname(normalized);
626
+ if (!this.dirs.has(parent)) {
627
+ throw new Error(`ENOENT: no such file or directory, mkdir '${normalized}'`);
628
+ }
629
+ this.dirs.set(normalized, this.allocateDirectoryInode().ino);
630
+ this.adjustParentDirectoryLinkCount(normalized, 1);
631
+ }
632
+ adjustParentDirectoryLinkCount(path, delta) {
633
+ const normalized = normalizePath(path);
634
+ if (normalized === "/")
635
+ return;
636
+ const parent = dirname(normalized);
637
+ const parentIno = this.dirs.get(parent);
638
+ if (parentIno === undefined)
639
+ return;
640
+ if (delta > 0) {
641
+ this.inodeTable.incrementLinks(parentIno);
642
+ }
643
+ else {
644
+ this.inodeTable.decrementLinks(parentIno);
645
+ }
646
+ }
612
647
  }
613
648
  export function createInMemoryFileSystem() {
614
649
  return new InMemoryFileSystem();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secure-exec/core",
3
- "version": "0.2.0-rc.1",
3
+ "version": "0.2.0-rc.2",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "./dist/index.js",