@codyswann/lisa 4.70.3 → 4.71.0

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 (143) hide show
  1. package/all/copy-overwrite/scripts/lib/automation-provenance-contract.mjs +174 -0
  2. package/all/copy-overwrite/scripts/lib/automation-provenance-local.mjs +279 -0
  3. package/all/copy-overwrite/scripts/lib/gate-failure-diagnosis.mjs +17 -7
  4. package/all/copy-overwrite/scripts/lib/github-attestation-provider.mjs +241 -0
  5. package/all/copy-overwrite/scripts/lib/github-attestation-recovery.mjs +283 -0
  6. package/all/copy-overwrite/scripts/lib/github-attestation-verifier.mjs +244 -0
  7. package/all/copy-overwrite/scripts/lib/history-secret-evidence-shape.mjs +188 -0
  8. package/all/copy-overwrite/scripts/lib/history-secret-evidence.mjs +271 -0
  9. package/all/copy-overwrite/scripts/lib/history-secret-scanner.mjs +6 -296
  10. package/all/copy-overwrite/scripts/lib/npm-update-allocate.mjs +289 -0
  11. package/all/copy-overwrite/scripts/lib/npm-update-authorization.mjs +235 -0
  12. package/all/copy-overwrite/scripts/lib/npm-update-broker-client.mjs +92 -0
  13. package/all/copy-overwrite/scripts/lib/npm-update-checkpoint.mjs +241 -0
  14. package/all/copy-overwrite/scripts/lib/npm-update-contract.mjs +278 -0
  15. package/all/copy-overwrite/scripts/lib/npm-update-controller-broker.mjs +234 -0
  16. package/all/copy-overwrite/scripts/lib/npm-update-controller-factory.mjs +278 -0
  17. package/all/copy-overwrite/scripts/lib/npm-update-controller-recipe.mjs +135 -0
  18. package/all/copy-overwrite/scripts/lib/npm-update-execution-adapter.mjs +292 -0
  19. package/all/copy-overwrite/scripts/lib/npm-update-gate-hooks.mjs +214 -0
  20. package/all/copy-overwrite/scripts/lib/npm-update-gate-install.mjs +267 -0
  21. package/all/copy-overwrite/scripts/lib/npm-update-gate-proof.mjs +99 -0
  22. package/all/copy-overwrite/scripts/lib/npm-update-gate.mjs +300 -0
  23. package/all/copy-overwrite/scripts/lib/npm-update-gh-dispatch.mjs +182 -0
  24. package/all/copy-overwrite/scripts/lib/npm-update-gh-grants.mjs +156 -0
  25. package/all/copy-overwrite/scripts/lib/npm-update-gh-requests.mjs +221 -0
  26. package/all/copy-overwrite/scripts/lib/npm-update-github.mjs +214 -0
  27. package/all/copy-overwrite/scripts/lib/npm-update-helper-graph.mjs +238 -0
  28. package/all/copy-overwrite/scripts/lib/npm-update-helper-inventory.mjs +126 -0
  29. package/all/copy-overwrite/scripts/lib/npm-update-helper.mjs +292 -0
  30. package/all/copy-overwrite/scripts/lib/npm-update-hook-installation.mjs +116 -0
  31. package/all/copy-overwrite/scripts/lib/npm-update-hook-preload.mjs +153 -0
  32. package/all/copy-overwrite/scripts/lib/npm-update-hook-provider.mjs +207 -0
  33. package/all/copy-overwrite/scripts/lib/npm-update-hook-read-client.mjs +164 -0
  34. package/all/copy-overwrite/scripts/lib/npm-update-hosted-gate.mjs +272 -0
  35. package/all/copy-overwrite/scripts/lib/npm-update-hosted-hook.mjs +157 -0
  36. package/all/copy-overwrite/scripts/lib/npm-update-invariants.mjs +28 -0
  37. package/all/copy-overwrite/scripts/lib/npm-update-isolation.mjs +11 -0
  38. package/all/copy-overwrite/scripts/lib/npm-update-leaf-contract.mjs +171 -0
  39. package/all/copy-overwrite/scripts/lib/npm-update-leaf.mjs +194 -0
  40. package/all/copy-overwrite/scripts/lib/npm-update-native-process.mjs +155 -0
  41. package/all/copy-overwrite/scripts/lib/npm-update-npm.mjs +190 -0
  42. package/all/copy-overwrite/scripts/lib/npm-update-object.mjs +112 -0
  43. package/all/copy-overwrite/scripts/lib/npm-update-orchestrator.mjs +273 -0
  44. package/all/copy-overwrite/scripts/lib/npm-update-owner.mjs +292 -0
  45. package/all/copy-overwrite/scripts/lib/npm-update-prepare.mjs +263 -0
  46. package/all/copy-overwrite/scripts/lib/npm-update-process-core.mjs +179 -0
  47. package/all/copy-overwrite/scripts/lib/npm-update-process.mjs +16 -0
  48. package/all/copy-overwrite/scripts/lib/npm-update-publication.mjs +258 -0
  49. package/all/copy-overwrite/scripts/lib/npm-update-publish.mjs +258 -0
  50. package/all/copy-overwrite/scripts/lib/npm-update-quality.mjs +153 -0
  51. package/all/copy-overwrite/scripts/lib/npm-update-recovery.mjs +264 -0
  52. package/all/copy-overwrite/scripts/lib/npm-update-runtime-archive.mjs +204 -0
  53. package/all/copy-overwrite/scripts/lib/npm-update-runtime-graph.mjs +185 -0
  54. package/all/copy-overwrite/scripts/lib/npm-update-runtime-transport.mjs +221 -0
  55. package/all/copy-overwrite/scripts/lib/npm-update-runtime.mjs +284 -0
  56. package/all/copy-overwrite/scripts/lib/npm-update-tool-launcher.mjs +287 -0
  57. package/all/copy-overwrite/scripts/lib/npm-update-worker-environment.mjs +161 -0
  58. package/all/copy-overwrite/scripts/lib/npm-update-worker-inspection.mjs +142 -0
  59. package/all/copy-overwrite/scripts/lib/npm-update-worker-lifecycle.mjs +256 -0
  60. package/all/copy-overwrite/scripts/lib/npm-update-worker-policy.mjs +266 -0
  61. package/all/copy-overwrite/scripts/lib/process-tree-runner.mjs +230 -36
  62. package/all/copy-overwrite/scripts/lisa-automation-provenance.mjs +161 -0
  63. package/all/copy-overwrite/scripts/lisa-history-secrets.mjs +8 -1
  64. package/all/copy-overwrite/scripts/lisa-hooks/block-no-verify.sh +89 -5
  65. package/all/copy-overwrite/scripts/lisa-npm-updater.mjs +204 -0
  66. package/all/copy-overwrite/scripts/lisa-run-gates.mjs +48 -123
  67. package/all/copy-overwrite/scripts/lisa-work-item.mjs +107 -41
  68. package/all/copy-overwrite/scripts/npm-updater-gate-runtime.json +1 -0
  69. package/all/copy-overwrite/scripts/npm-updater-gate-supervisor.c +121 -0
  70. package/all/copy-overwrite/scripts/npm-updater-gate.Dockerfile +49 -0
  71. package/all/copy-overwrite/scripts/npm-updater-helper-graph.json +1085 -0
  72. package/dist/codex/scripts/block-no-verify.sh +89 -5
  73. package/dist/core/lisa-owned-hash-ledger.d.ts.map +1 -1
  74. package/dist/core/lisa-owned-hash-ledger.js +620 -0
  75. package/dist/core/lisa-owned-hash-ledger.js.map +1 -1
  76. package/dist/core/nightly-e2e-guard-behavior-certificate.js +2 -2
  77. package/dist/core/upstream-evidence-manifest.d.ts.map +1 -1
  78. package/dist/core/upstream-evidence-manifest.js +165 -13
  79. package/dist/core/upstream-evidence-manifest.js.map +1 -1
  80. package/dist/opencode/plugin-templates/block-no-verify.sh +89 -5
  81. package/package.json +4 -4
  82. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  83. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  84. package/plugins/lisa/hooks/block-no-verify.agy.sh +89 -5
  85. package/plugins/lisa/hooks/block-no-verify.sh +89 -5
  86. package/plugins/lisa-agy/hooks/block-no-verify.agy.sh +89 -5
  87. package/plugins/lisa-agy/plugin.json +1 -1
  88. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  89. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  90. package/plugins/lisa-cdk-agy/plugin.json +1 -1
  91. package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
  93. package/plugins/lisa-copilot/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-copilot/hooks/block-no-verify.sh +89 -5
  95. package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
  96. package/plugins/lisa-cursor/hooks/block-no-verify.sh +89 -5
  97. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  98. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  99. package/plugins/lisa-expo-agy/plugin.json +1 -1
  100. package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
  101. package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
  102. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  103. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  104. package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
  105. package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
  106. package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
  107. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  108. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  109. package/plugins/lisa-nestjs-agy/plugin.json +1 -1
  110. package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
  111. package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
  112. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  113. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  114. package/plugins/lisa-openclaw-agy/plugin.json +1 -1
  115. package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
  116. package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
  117. package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
  118. package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
  119. package/plugins/lisa-phaser-agy/plugin.json +1 -1
  120. package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
  121. package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
  122. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  123. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  124. package/plugins/lisa-rails-agy/plugin.json +1 -1
  125. package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
  126. package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
  127. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  128. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  129. package/plugins/lisa-typescript-agy/plugin.json +1 -1
  130. package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
  131. package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
  132. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  133. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  134. package/plugins/lisa-wiki-agy/plugin.json +1 -1
  135. package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
  136. package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
  137. package/plugins/src/base/hooks/block-no-verify.agy.sh +89 -5
  138. package/plugins/src/base/hooks/block-no-verify.sh +89 -5
  139. package/scripts/check-shipped-surface-removals.mjs +96 -24
  140. package/scripts/generate-npm-updater-helper-graph.mjs +184 -0
  141. package/scripts/two-channel-couplings.json +31 -7
  142. package/typescript/copy-contents/.husky/commit-msg +18 -0
  143. package/typescript/copy-overwrite/.prettierignore +2 -0
@@ -1016,7 +1016,7 @@ export function deployBranchEnvironments(config) {
1016
1016
  return new Map([[branch, PRODUCTION]]);
1017
1017
  }
1018
1018
 
1019
- function trackerContract(config = readConfig()) {
1019
+ export function trackerContract(config = readConfig()) {
1020
1020
  const provider = requireString(config.tracker, "tracker").toLowerCase();
1021
1021
  const identityRepo = currentRepoIdentity(config);
1022
1022
  if (provider === "github") {
@@ -1483,6 +1483,38 @@ export function soleWorkItem(text, contract, subject) {
1483
1483
  return refs[0];
1484
1484
  }
1485
1485
 
1486
+ /**
1487
+ * Resolve a required binding through the authoritative tracker contract.
1488
+ * Consumers supplying trusted committed configuration avoid reparsing trailers
1489
+ * or treating raw `current` output as a checked branch/queue identity.
1490
+ * This does not change ordinary offline/live validation semantics.
1491
+ * @param {string} message Complete final commit message.
1492
+ * @param {object} [options] Trusted configuration and strict live requirement.
1493
+ * @returns {object} Canonical identity and optional actual provider evidence.
1494
+ */
1495
+ export function resolveWorkItemContext(message, options = {}) {
1496
+ const contract = trackerContract(options.config ?? readConfig());
1497
+ const state = readState(false);
1498
+ const ref = soleWorkItem(message, contract, COMMIT_SUBJECT);
1499
+ assertStateBranch(state);
1500
+ assertStateMatches(ref, contract);
1501
+ if (options.requireLive && contract.provider !== "github") {
1502
+ throw new TrackingError("Automation provenance requires GitHub tracking");
1503
+ }
1504
+ const issue = options.requireLive
1505
+ ? githubIssue(ref, contract, options.execute ?? run)
1506
+ : undefined;
1507
+ return {
1508
+ ref,
1509
+ provider: contract.provider,
1510
+ repository: contract.repository,
1511
+ identityRepo: contract.identityRepo,
1512
+ branch: state.branch,
1513
+ lifecycle: contract.lifecycle,
1514
+ issue,
1515
+ };
1516
+ }
1517
+
1486
1518
  /**
1487
1519
  * The one work item a COMMIT message names.
1488
1520
  * @param {string} message Commit message.
@@ -1843,28 +1875,34 @@ function typeFromLabels(labels) {
1843
1875
  * @param {string|number} number GitHub issue number.
1844
1876
  * @returns {string[]} State of every sub-issue.
1845
1877
  */
1846
- function githubHierarchy(ref, contract, number) {
1878
+ /** Literal typed GraphQL fields are shared by the canonical reader and closed native transport. */
1879
+ export function githubHierarchyArgs(contract, number, after) {
1847
1880
  const [owner, repo] = contract.repository.split("/");
1848
1881
  const query =
1849
1882
  "query($owner:String!,$repo:String!,$number:Int!,$after:String){repository(owner:$owner,name:$repo){issue(number:$number){subIssues(first:100,after:$after){nodes{state}pageInfo{hasNextPage endCursor}}}}}";
1883
+ const args = [
1884
+ "api",
1885
+ "graphql",
1886
+ "-f",
1887
+ `query=${query}`,
1888
+ "-F",
1889
+ `owner=${owner}`,
1890
+ "-F",
1891
+ `repo=${repo}`,
1892
+ "-F",
1893
+ `number=${number}`,
1894
+ ];
1895
+ if (after) args.push("-F", `after=${after}`);
1896
+ return args;
1897
+ }
1898
+
1899
+ function githubHierarchy(ref, contract, number, execute = run) {
1850
1900
  const states = [];
1851
1901
  const cursor = { after: null };
1852
1902
 
1853
1903
  do {
1854
- const args = [
1855
- "api",
1856
- "graphql",
1857
- "-f",
1858
- `query=${query}`,
1859
- "-F",
1860
- `owner=${owner}`,
1861
- "-F",
1862
- `repo=${repo}`,
1863
- "-F",
1864
- `number=${number}`,
1865
- ];
1866
- if (cursor.after) args.push("-F", `after=${cursor.after}`);
1867
- const result = run("gh", args, { allowFailure: true });
1904
+ const args = githubHierarchyArgs(contract, number, cursor.after);
1905
+ const result = execute("gh", args, { allowFailure: true });
1868
1906
  if (result.status !== 0) throw githubFailure(result, ref);
1869
1907
 
1870
1908
  const response = safeJson(result.stdout, `GitHub issue ${ref} hierarchy`);
@@ -1952,20 +1990,25 @@ export function resetGhVersionCheck() {
1952
1990
  ghVersionChecked = false;
1953
1991
  }
1954
1992
 
1955
- function githubIssue(ref, contract) {
1956
- assertGhVersion();
1993
+ /** The body is required by recovery; all ordinary canonical validation fields remain present. */
1994
+ export function githubIssueViewArgs(repository, number) {
1995
+ return [
1996
+ "issue",
1997
+ "view",
1998
+ number,
1999
+ "--repo",
2000
+ repository,
2001
+ "--json",
2002
+ "number,url,state,body,labels,comments,closedByPullRequestsReferences",
2003
+ ];
2004
+ }
2005
+
2006
+ function githubIssue(ref, contract, execute = run) {
2007
+ assertGhVersion(execute);
1957
2008
  const number = ref.slice(ref.lastIndexOf("#") + 1);
1958
- const result = run(
2009
+ const result = execute(
1959
2010
  "gh",
1960
- [
1961
- "issue",
1962
- "view",
1963
- number,
1964
- "--repo",
1965
- contract.repository,
1966
- "--json",
1967
- "number,url,state,labels,comments,closedByPullRequestsReferences",
1968
- ],
2011
+ githubIssueViewArgs(contract.repository, number),
1969
2012
  {
1970
2013
  allowFailure: true,
1971
2014
  }
@@ -1985,7 +2028,7 @@ function githubIssue(ref, contract) {
1985
2028
  assertLeaf(
1986
2029
  ref,
1987
2030
  typeFromLabels(issue.labels),
1988
- githubHierarchy(ref, contract, number)
2031
+ githubHierarchy(ref, contract, number, execute)
1989
2032
  );
1990
2033
  return issue;
1991
2034
  }
@@ -2410,7 +2453,7 @@ export function textContainsBacklink(value, prUrl) {
2410
2453
  * @param {string} prUrl Pull request URL.
2411
2454
  * @returns {string} The comment body.
2412
2455
  */
2413
- function backlinkBody(prUrl) {
2456
+ export function backlinkBody(prUrl) {
2414
2457
  return `${MARKER} ${prUrl}`;
2415
2458
  }
2416
2459
 
@@ -2502,7 +2545,7 @@ function managedBacklinkTarget(body) {
2502
2545
  * @returns {{mine: unknown, others: number}} This PR's comment, and how many
2503
2546
  * managed comments belong to OTHER pull requests.
2504
2547
  */
2505
- function partitionBacklinks(comments, prUrl, bodyOf) {
2548
+ export function partitionBacklinks(comments, prUrl, bodyOf) {
2506
2549
  const mine = [];
2507
2550
  let others = 0;
2508
2551
  for (const comment of comments) {
@@ -2538,6 +2581,33 @@ export function backlinkReport(outcome, ref, prUrl) {
2538
2581
  );
2539
2582
  }
2540
2583
 
2584
+ /** Complete canonical pagination is shared by the writer and its scoped controller. */
2585
+ export function githubBacklinkListArgs(repository, number) {
2586
+ if (
2587
+ !/^[\w.-]+\/[\w.-]+$/.test(repository) ||
2588
+ !/^[1-9]\d*$/.test(String(number))
2589
+ )
2590
+ throw new Error("invalid canonical GitHub backlink repository/issue");
2591
+ return [
2592
+ "api",
2593
+ "--paginate",
2594
+ "--slurp",
2595
+ `repos/${repository}/issues/${number}/comments?per_page=100`,
2596
+ ];
2597
+ }
2598
+
2599
+ /** Complete slurped pages remain bounded and are shared by the canonical writer and controller grant. */
2600
+ export function githubBacklinkComments(pages) {
2601
+ if (
2602
+ !Array.isArray(pages) ||
2603
+ pages.length < 1 ||
2604
+ pages.length > 100 ||
2605
+ pages.some(page => !Array.isArray(page) || page.length > 100)
2606
+ )
2607
+ throw new Error("invalid or over-bound GitHub backlink pages");
2608
+ return pages.flat();
2609
+ }
2610
+
2541
2611
  /**
2542
2612
  * Establish the backlink on a GitHub issue.
2543
2613
  * @param {string} ref Canonical `owner/repo#number` reference.
@@ -2547,17 +2617,13 @@ export function backlinkReport(outcome, ref, prUrl) {
2547
2617
  */
2548
2618
  function githubBacklink(ref, prUrl) {
2549
2619
  const [repository, number] = ref.split("#");
2550
- const listing = run(
2551
- "gh",
2552
- [
2553
- "api",
2554
- "--paginate",
2555
- `repos/${repository}/issues/${number}/comments?per_page=100`,
2556
- ],
2557
- { allowFailure: true }
2558
- );
2620
+ const listing = run("gh", githubBacklinkListArgs(repository, number), {
2621
+ allowFailure: true,
2622
+ });
2559
2623
  if (listing.status !== 0) throw githubFailure(listing, ref);
2560
- const comments = safeJson(listing.stdout, `GitHub comments on ${ref}`);
2624
+ const comments = githubBacklinkComments(
2625
+ safeJson(listing.stdout, `GitHub comments on ${ref}`)
2626
+ );
2561
2627
  // This listing is the read-before-write, and it is fetched here rather than
2562
2628
  // passed in for exactly that reason: the version you last wrote is not the
2563
2629
  // version that is live on a surface other agents also comment on.
@@ -0,0 +1 @@
1
+ {"platforms":[],"recipeSha256":"260d194175737ed1cfdf6d208696bb58d035a45b2fd27c83a54e75be65ab5cb0","seccomp":{"profile":{"archMap":[{"architecture":"SCMP_ARCH_X86_64","subArchitectures":["SCMP_ARCH_X86","SCMP_ARCH_X32"]},{"architecture":"SCMP_ARCH_AARCH64","subArchitectures":["SCMP_ARCH_ARM"]},{"architecture":"SCMP_ARCH_MIPS64","subArchitectures":["SCMP_ARCH_MIPS","SCMP_ARCH_MIPS64N32"]},{"architecture":"SCMP_ARCH_MIPS64N32","subArchitectures":["SCMP_ARCH_MIPS","SCMP_ARCH_MIPS64"]},{"architecture":"SCMP_ARCH_MIPSEL64","subArchitectures":["SCMP_ARCH_MIPSEL","SCMP_ARCH_MIPSEL64N32"]},{"architecture":"SCMP_ARCH_MIPSEL64N32","subArchitectures":["SCMP_ARCH_MIPSEL","SCMP_ARCH_MIPSEL64"]},{"architecture":"SCMP_ARCH_S390X","subArchitectures":["SCMP_ARCH_S390"]},{"architecture":"SCMP_ARCH_RISCV64","subArchitectures":null},{"architecture":"SCMP_ARCH_LOONGARCH64","subArchitectures":null}],"defaultAction":"SCMP_ACT_ERRNO","defaultErrnoRet":1,"syscalls":[{"action":"SCMP_ACT_ALLOW","names":["accept","accept4","access","adjtimex","alarm","bind","brk","cachestat","capget","capset","chdir","chmod","chown","chown32","clock_adjtime","clock_adjtime64","clock_getres","clock_getres_time64","clock_gettime","clock_gettime64","clock_nanosleep","clock_nanosleep_time64","close","close_range","connect","copy_file_range","creat","dup","dup2","dup3","epoll_create","epoll_create1","epoll_ctl","epoll_ctl_old","epoll_pwait","epoll_pwait2","epoll_wait","epoll_wait_old","eventfd","eventfd2","execve","execveat","exit","exit_group","faccessat","faccessat2","fadvise64","fadvise64_64","fallocate","fanotify_mark","fchdir","fchmod","fchmodat","fchmodat2","fchown","fchown32","fchownat","fcntl","fcntl64","fdatasync","fgetxattr","flistxattr","flock","fork","fremovexattr","fsetxattr","fstat","fstat64","fstatat64","fstatfs","fstatfs64","fsync","ftruncate","ftruncate64","futex","futex_requeue","futex_time64","futex_wait","futex_waitv","futex_wake","futimesat","getcpu","getcwd","getdents","getdents64","getegid","getegid32","geteuid","geteuid32","getgid","getgid32","getgroups","getgroups32","getitimer","getpeername","getpgid","getpgrp","getpid","getppid","getpriority","getrandom","getresgid","getresgid32","getresuid","getresuid32","getrlimit","get_robust_list","getrusage","getsid","getsockname","getsockopt","get_thread_area","gettid","gettimeofday","getuid","getuid32","getxattr","getxattrat","inotify_add_watch","inotify_init","inotify_init1","inotify_rm_watch","io_cancel","ioctl","io_destroy","io_getevents","io_pgetevents","io_pgetevents_time64","ioprio_get","ioprio_set","io_setup","io_submit","ipc","kill","landlock_add_rule","landlock_create_ruleset","landlock_restrict_self","lchown","lchown32","lgetxattr","link","linkat","listen","listmount","listxattr","listxattrat","llistxattr","_llseek","lremovexattr","lseek","lsetxattr","lstat","lstat64","madvise","map_shadow_stack","membarrier","memfd_create","memfd_secret","mincore","mkdir","mkdirat","mknod","mknodat","mlock","mlock2","mlockall","mmap","mmap2","mprotect","mq_getsetattr","mq_notify","mq_open","mq_timedreceive","mq_timedreceive_time64","mq_timedsend","mq_timedsend_time64","mq_unlink","mremap","mseal","msgctl","msgget","msgrcv","msgsnd","msync","munlock","munlockall","munmap","name_to_handle_at","nanosleep","newfstatat","_newselect","open","openat","openat2","pause","pidfd_open","pidfd_send_signal","pipe","pipe2","pkey_alloc","pkey_free","pkey_mprotect","poll","ppoll","ppoll_time64","prctl","pread64","preadv","preadv2","prlimit64","process_mrelease","pselect6","pselect6_time64","pwrite64","pwritev","pwritev2","read","readahead","readlink","readlinkat","readv","recv","recvfrom","recvmmsg","recvmmsg_time64","recvmsg","remap_file_pages","removexattr","removexattrat","rename","renameat","renameat2","restart_syscall","riscv_hwprobe","rmdir","rseq","rt_sigaction","rt_sigpending","rt_sigprocmask","rt_sigqueueinfo","rt_sigreturn","rt_sigsuspend","rt_sigtimedwait","rt_sigtimedwait_time64","rt_tgsigqueueinfo","sched_getaffinity","sched_getattr","sched_getparam","sched_get_priority_max","sched_get_priority_min","sched_getscheduler","sched_rr_get_interval","sched_rr_get_interval_time64","sched_setaffinity","sched_setattr","sched_setparam","sched_setscheduler","sched_yield","seccomp","select","semctl","semget","semop","semtimedop","semtimedop_time64","send","sendfile","sendfile64","sendmmsg","sendmsg","sendto","setfsgid","setfsgid32","setfsuid","setfsuid32","setgid","setgid32","setgroups","setgroups32","setitimer","setpgid","setpriority","setregid","setregid32","setresgid","setresgid32","setresuid","setresuid32","setreuid","setreuid32","setrlimit","set_robust_list","setsid","setsockopt","set_thread_area","set_tid_address","setuid","setuid32","setxattr","setxattrat","shmat","shmctl","shmdt","shmget","shutdown","sigaltstack","signalfd","signalfd4","sigprocmask","sigreturn","socketcall","socketpair","splice","stat","stat64","statfs","statfs64","statmount","statx","symlink","symlinkat","sync","sync_file_range","syncfs","sysinfo","tee","tgkill","time","timer_create","timer_delete","timer_getoverrun","timer_gettime","timer_gettime64","timer_settime","timer_settime64","timerfd_create","timerfd_gettime","timerfd_gettime64","timerfd_settime","timerfd_settime64","times","tkill","truncate","truncate64","ugetrlimit","umask","uname","unlink","unlinkat","uretprobe","utime","utimensat","utimensat_time64","utimes","vfork","vmsplice","wait4","waitid","waitpid","write","writev"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_LT","value":3}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":7}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":8}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":9}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":10}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":13}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":14}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":15}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":16}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":17}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":18}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":21}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":22}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":24}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":26}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":27}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":28}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":29}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":30}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":31}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":32}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":33}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":35}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":36}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":39}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":41}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":42}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":43}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":44}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":45}],"names":["socket"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":0}],"names":["personality"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":8}],"names":["personality"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":131072}],"names":["personality"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":131080}],"names":["personality"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_EQ","value":4294967295}],"names":["personality"]},{"action":"SCMP_ACT_ALLOW","includes":{"arches":["ppc64le"]},"names":["sync_file_range2","swapcontext"]},{"action":"SCMP_ACT_ALLOW","includes":{"arches":["arm","arm64"]},"names":["arm_fadvise64_64","arm_sync_file_range","sync_file_range2","breakpoint","cacheflush","set_tls"]},{"action":"SCMP_ACT_ALLOW","includes":{"arches":["amd64","x32"]},"names":["arch_prctl"]},{"action":"SCMP_ACT_ALLOW","includes":{"arches":["amd64","x32","x86"]},"names":["modify_ldt"]},{"action":"SCMP_ACT_ALLOW","includes":{"arches":["s390","s390x"]},"names":["s390_pci_mmio_read","s390_pci_mmio_write","s390_runtime_instr"]},{"action":"SCMP_ACT_ALLOW","includes":{"arches":["riscv64"]},"names":["riscv_flush_icache"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_ADMIN"]},"names":["clone","clone3","fanotify_init","fsconfig","fsmount","fsopen","fspick","lookup_dcookie","lsm_get_self_attr","lsm_list_modules","lsm_set_self_attr","mount_setattr","move_mount","open_tree","perf_event_open","quotactl","quotactl_fd","setdomainname","sethostname","syslog","umount"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":0,"op":"SCMP_CMP_MASKED_EQ","value":2114060288}],"excludes":{"arches":["s390","s390x"],"caps":["CAP_SYS_ADMIN"]},"names":["clone"]},{"action":"SCMP_ACT_ALLOW","args":[{"index":1,"op":"SCMP_CMP_MASKED_EQ","value":2114060288}],"comment":"s390 parameter ordering for clone is different","excludes":{"caps":["CAP_SYS_ADMIN"]},"includes":{"arches":["s390","s390x"]},"names":["clone"]},{"action":"SCMP_ACT_ERRNO","errnoRet":38,"excludes":{"caps":["CAP_SYS_ADMIN"]},"names":["clone3"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_BOOT"]},"names":["reboot"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_CHROOT"]},"names":["chroot"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_MODULE"]},"names":["delete_module","init_module","finit_module"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_PACCT"]},"names":["acct"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_PTRACE"]},"names":["kcmp","pidfd_getfd","process_madvise"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_RAWIO"]},"names":["iopl","ioperm"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_TIME"]},"names":["settimeofday","stime","clock_settime","clock_settime64"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_TTY_CONFIG"]},"names":["vhangup"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYS_NICE"]},"names":["get_mempolicy","mbind","set_mempolicy","set_mempolicy_home_node"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_SYSLOG"]},"names":["syslog"]},{"action":"SCMP_ACT_ALLOW","includes":{"caps":["CAP_PERFMON"]},"names":["perf_event_open"]}]},"sha256":"5c3af609ff2ca4186b0c0d8eef81c75980d0ca67224ec4a0052ea44e710331e2"},"services":[],"supervisorSha256":"2506d8750694b93169f70e71d4c9dd68de0a0724bf9622bf9e15fa6cd2404b69","version":1}
@@ -0,0 +1,121 @@
1
+ /* This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ * Do not edit directly — durable changes belong upstream in Lisa. */
3
+ /* The fixed nonroot PID1 retains deadline/verdict authority across child exec. */
4
+ #define _POSIX_C_SOURCE 200809L
5
+ #include <errno.h>
6
+ #include <signal.h>
7
+ #include <stdio.h>
8
+ #include <stdlib.h>
9
+ #include <string.h>
10
+ #include <sys/prctl.h>
11
+ #include <sys/types.h>
12
+ #include <sys/wait.h>
13
+ #include <time.h>
14
+ #include <unistd.h>
15
+
16
+ static volatile sig_atomic_t interrupted = 0;
17
+
18
+ static void interruption(int signal_number) {
19
+ interrupted = signal_number;
20
+ }
21
+
22
+ static long long milliseconds(void) {
23
+ struct timespec now;
24
+ if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) return -1;
25
+ return (long long)now.tv_sec * 1000 + now.tv_nsec / 1000000;
26
+ }
27
+
28
+ static void pause_briefly(void) {
29
+ const struct timespec delay = {0, 20000000};
30
+ (void)nanosleep(&delay, NULL);
31
+ }
32
+
33
+ static int deadline_argument(const char *value) {
34
+ if (!value || !*value) return -1;
35
+ for (const char *p = value; *p; ++p) {
36
+ if (*p < '0' || *p > '9') return -1;
37
+ }
38
+ errno = 0;
39
+ char *end = NULL;
40
+ long result = strtol(value, &end, 10);
41
+ if (errno || !end || *end || result < 1 || result > 1800000) return -1;
42
+ return (int)result;
43
+ }
44
+
45
+ /* Namespace-wide signals are used only after main has established actual PID1. */
46
+ static int cleanup_children(void) {
47
+ long long started = milliseconds();
48
+ if (started < 0 || (kill(-1, SIGTERM) != 0 && errno != ESRCH)) return -1;
49
+ int killed = 0;
50
+ for (;;) {
51
+ int status;
52
+ pid_t reaped;
53
+ do { reaped = waitpid(-1, &status, WNOHANG); } while (reaped > 0);
54
+ if (reaped < 0 && errno == ECHILD) return 0;
55
+ if (reaped < 0 && errno != EINTR) return -1;
56
+ long long now = milliseconds();
57
+ if (now < 0 || now - started >= 4000) return -1;
58
+ if (!killed && now - started >= 2000) {
59
+ if (kill(-1, SIGKILL) != 0 && errno != ESRCH) return -1;
60
+ killed = 1;
61
+ }
62
+ pause_briefly();
63
+ }
64
+ }
65
+
66
+ /* Only a real waitpid result can be the original command's successful verdict. */
67
+ static int await_command(pid_t child, int duration) {
68
+ long long started = milliseconds();
69
+ if (started < 0) return 125;
70
+ for (;;) {
71
+ int status = 0;
72
+ pid_t result = waitpid(child, &status, WNOHANG);
73
+ if (interrupted) return 128 + interrupted;
74
+ if (result == child) {
75
+ if (WIFEXITED(status)) return WEXITSTATUS(status);
76
+ if (WIFSIGNALED(status)) return 128 + WTERMSIG(status);
77
+ return 125;
78
+ }
79
+ if (result < 0 && errno != EINTR) return 125;
80
+ long long now = milliseconds();
81
+ if (now < 0) return 125;
82
+ if (now - started >= duration) return 124;
83
+ pause_briefly();
84
+ }
85
+ }
86
+
87
+ int main(int argc, char **argv) {
88
+ if (argc == 2 && strcmp(argv[1], "--version") == 0) {
89
+ puts("lisa-npm-supervisor 1");
90
+ return 0;
91
+ }
92
+ int duration = argc >= 4 ? deadline_argument(argv[1]) : -1;
93
+ if (duration < 0 || strcmp(argv[2], "--") != 0 || argv[3][0] != '/' ||
94
+ getpid() != 1 || getuid() == 0 || getgid() == 0) {
95
+ fputs("lisa supervisor: invalid nonroot PID1 invocation\n", stderr);
96
+ return 125;
97
+ }
98
+ if (prctl(PR_SET_DUMPABLE, 0) != 0 || prctl(PR_GET_DUMPABLE) != 0) {
99
+ fputs("lisa supervisor: dumpability protection unavailable\n", stderr);
100
+ return 125;
101
+ }
102
+ struct sigaction action;
103
+ memset(&action, 0, sizeof action);
104
+ action.sa_handler = interruption;
105
+ if (sigemptyset(&action.sa_mask) != 0 || sigaction(SIGTERM, &action, NULL) != 0 ||
106
+ sigaction(SIGINT, &action, NULL) != 0 || sigaction(SIGHUP, &action, NULL) != 0) return 125;
107
+ fprintf(stderr, "lisa supervisor: pid=1 uid=%lu gid=%lu dumpable=0\n",
108
+ (unsigned long)getuid(), (unsigned long)getgid());
109
+ if (fflush(stderr) != 0) return 125;
110
+ pid_t child = fork();
111
+ if (child < 0) return 125;
112
+ if (child == 0) {
113
+ if (setpgid(0, 0) != 0) _exit(125);
114
+ execv(argv[3], argv + 3);
115
+ _exit(errno == ENOENT ? 127 : 126);
116
+ }
117
+ int result = await_command(child, duration);
118
+ if (cleanup_children() != 0) return 125;
119
+ if (interrupted) return 128 + interrupted;
120
+ return result;
121
+ }
@@ -0,0 +1,49 @@
1
+ # This file is managed by Lisa and IS replaced on each `lisa` run.
2
+ # Do not edit directly — durable changes belong upstream in Lisa.
3
+ # Trusted fixed recipe only. Application/package files never enter this build context.
4
+ ARG TARGETARCH
5
+ FROM --platform=linux/amd64 node@sha256:25330af3531fb5e23318554a0aa911125b6e91b1b777edf7655501d207c067a2 AS node-amd64
6
+ FROM --platform=linux/arm64 node@sha256:a0ddbc73510e98f5e824fd64266ffe1c2c343ba9cf260d95ca2985ad632a3f3e AS node-arm64
7
+ FROM node-${TARGETARCH} AS node-runtime
8
+ FROM --platform=linux/amd64 ruby@sha256:bcb412ff5c2bdace9af8171c410745e227aea813fa951caab539b2f18623d239 AS ruby-amd64
9
+ FROM --platform=linux/arm64 ruby@sha256:2836fb28bf6f9f1d88015861df0b52424daeb499f2816ed9e4ef31c3bc181909 AS ruby-arm64
10
+ FROM ruby-${TARGETARCH}
11
+ ARG TARGETARCH
12
+ COPY --from=node-runtime /usr/local/bin/node /opt/node/bin/node
13
+ COPY --from=node-runtime /usr/local/lib/node_modules /opt/node/lib/node_modules
14
+ ENV PATH=/opt/node/bin:/usr/local/bin:/usr/bin:/bin
15
+ RUN apt-get update && apt-get install --yes --no-install-recommends \
16
+ bash ca-certificates curl git unzip passwd build-essential pkg-config libmariadb-dev libmariadb-dev-compat libyaml-dev libvips42 libxml2-dev libxslt1-dev \
17
+ && rm -rf /var/lib/apt/lists/*
18
+ COPY npm-updater-gate-supervisor.c /tmp/npm-updater-gate-supervisor.c
19
+ RUN cc -std=c11 -Wall -Wextra -Werror -O2 /tmp/npm-updater-gate-supervisor.c \
20
+ -o /usr/local/bin/lisa-npm-supervisor && rm /tmp/npm-updater-gate-supervisor.c
21
+ RUN set -eu; \
22
+ curl --fail --location --silent --show-error https://registry.npmjs.org/npm/-/npm-11.21.0.tgz --output /tmp/npm.tgz; \
23
+ test "$(openssl dgst -sha512 -binary /tmp/npm.tgz | base64 -w0)" = 'Zov8KhamNneiLdELtj5YALtNmJW4L4fCLTzjfpzXG2w6MSHcf0UxgdlK5uloCuksWT+7mGUU7wi79cO6RqivPg=='; \
24
+ mkdir /opt/npm; tar -xzf /tmp/npm.tgz --strip-components=1 -C /opt/npm; \
25
+ ln -s /opt/npm/bin/npm-cli.js /opt/node/bin/npm; \
26
+ curl --fail --location --silent --show-error https://rubygems.org/downloads/bundler-2.4.10.gem --output /tmp/bundler.gem; \
27
+ echo 'b9806fa944063a6a8676a8f9ed4c7c776a36c3bc82f26c5760867a0285b4a121 /tmp/bundler.gem' | sha256sum --check --strict; \
28
+ gem install --local /tmp/bundler.gem --no-document; \
29
+ rm /tmp/npm.tgz /tmp/bundler.gem
30
+ RUN set -eu; \
31
+ case "$TARGETARCH" in \
32
+ amd64) bun_arch=x64; bun_sha=0322b17f0722da76a64298aad498225aedcbf6df1008a1dee45e16ecb226a3f1; gh_arch=amd64; gh_sha=83d5c2ccad5498f58bf6368acb1ab32588cf43ab3a4b1c301bf36328b1c8bd60; leaks_arch=x64; leaks_sha=551f6fc83ea457d62a0d98237cbad105af8d557003051f41f3e7ca7b3f2470eb ;; \
33
+ arm64) bun_arch=aarch64; bun_sha=4e9deb6814a7ec7f68725ddd97d0d7b4065bcda9a850f69d497567e995a7fa33; gh_arch=arm64; gh_sha=06f86ec7103d41993b76cd78072f43595c34aaa56506d971d9860e67140bf909; leaks_arch=arm64; leaks_sha=e4a487ee7ccd7d3a7f7ec08657610aa3606637dab924210b3aee62570fb4b080 ;; \
34
+ *) echo 'Unsupported qualified architecture' >&2; exit 1 ;; \
35
+ esac; \
36
+ curl --fail --location --silent --show-error "https://github.com/oven-sh/bun/releases/download/bun-v1.3.8/bun-linux-${bun_arch}.zip" --output /tmp/bun.zip; \
37
+ echo "${bun_sha} /tmp/bun.zip" | sha256sum --check --strict; \
38
+ unzip -p /tmp/bun.zip "bun-linux-${bun_arch}/bun" > /usr/local/bin/bun; chmod 755 /usr/local/bin/bun; \
39
+ curl --fail --location --silent --show-error "https://github.com/cli/cli/releases/download/v2.96.0/gh_2.96.0_linux_${gh_arch}.tar.gz" --output /tmp/gh.tgz; \
40
+ echo "${gh_sha} /tmp/gh.tgz" | sha256sum --check --strict; \
41
+ tar -xzOf /tmp/gh.tgz "gh_2.96.0_linux_${gh_arch}/bin/gh" > /usr/local/bin/gh; chmod 755 /usr/local/bin/gh; \
42
+ curl --fail --location --silent --show-error "https://github.com/gitleaks/gitleaks/releases/download/v8.30.1/gitleaks_8.30.1_linux_${leaks_arch}.tar.gz" --output /tmp/gitleaks.tgz; \
43
+ echo "${leaks_sha} /tmp/gitleaks.tgz" | sha256sum --check --strict; \
44
+ tar -xzOf /tmp/gitleaks.tgz gitleaks > /usr/local/bin/gitleaks; chmod 755 /usr/local/bin/gitleaks; \
45
+ rm /tmp/bun.zip /tmp/gh.tgz /tmp/gitleaks.tgz; \
46
+ node --version; node /opt/npm/bin/npm-cli.js --version; ruby --version; bundle _2.4.10_ --version; \
47
+ /usr/sbin/useradd --uid 2001 --user-group --create-home candidate
48
+ USER 2001:2001
49
+ WORKDIR /workspace