fullcourtdefense-cli 1.21.31 → 1.21.32

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/dist/config.d.ts CHANGED
@@ -32,6 +32,16 @@ export interface PowershellHealth {
32
32
  checkedAt: string;
33
33
  }
34
34
  export declare function getPowershellHealth(): PowershellHealth | undefined;
35
+ /**
36
+ * PSCredential.GetNetworkCredential() instead of Marshal::SecureStringToBSTR:
37
+ * hardened fleets run PowerShell in Constrained Language Mode, where the
38
+ * [Runtime.InteropServices.Marshal] calls are forbidden (script fails ->
39
+ * exit 1 -> "Shield key not available" -> every hook 401s fail-closed). The
40
+ * PSCredential technique is on CLM's approved-type list and decrypts the same
41
+ * DPAPI-protected SecureString in every language mode. Exported so the CLM
42
+ * regression test runs this EXACT text inside a ConstrainedLanguage session.
43
+ */
44
+ export declare const DPAPI_DECRYPT_SNIPPET = "$secure=ConvertTo-SecureString -String $env:FCD_DPAPI_VALUE; (New-Object System.Management.Automation.PSCredential('fcd', $secure)).GetNetworkCredential().Password";
35
45
  export declare function getHomeConfigPath(): string;
36
46
  export declare function loadConfig(configPath?: string): BotGuardConfig;
37
47
  export declare function getDefaultConfigPath(): string;
package/dist/config.js CHANGED
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.DPAPI_DECRYPT_SNIPPET = void 0;
36
37
  exports.getPowershellHealth = getPowershellHealth;
37
38
  exports.getHomeConfigPath = getHomeConfigPath;
38
39
  exports.loadConfig = loadConfig;
@@ -204,8 +205,18 @@ function powershellDpapi(script, value) {
204
205
  function protectShieldKeyForCurrentWindowsUser(value) {
205
206
  return powershellDpapi('$secure=ConvertTo-SecureString -String $env:FCD_DPAPI_VALUE -AsPlainText -Force; ConvertFrom-SecureString -SecureString $secure', value);
206
207
  }
208
+ /**
209
+ * PSCredential.GetNetworkCredential() instead of Marshal::SecureStringToBSTR:
210
+ * hardened fleets run PowerShell in Constrained Language Mode, where the
211
+ * [Runtime.InteropServices.Marshal] calls are forbidden (script fails ->
212
+ * exit 1 -> "Shield key not available" -> every hook 401s fail-closed). The
213
+ * PSCredential technique is on CLM's approved-type list and decrypts the same
214
+ * DPAPI-protected SecureString in every language mode. Exported so the CLM
215
+ * regression test runs this EXACT text inside a ConstrainedLanguage session.
216
+ */
217
+ exports.DPAPI_DECRYPT_SNIPPET = "$secure=ConvertTo-SecureString -String $env:FCD_DPAPI_VALUE; (New-Object System.Management.Automation.PSCredential('fcd', $secure)).GetNetworkCredential().Password";
207
218
  function unprotectShieldKeyForCurrentWindowsUser(value) {
208
- return powershellDpapi('$secure=ConvertTo-SecureString -String $env:FCD_DPAPI_VALUE; $ptr=[Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure); try {[Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr)} finally {[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr)}', value);
219
+ return powershellDpapi(exports.DPAPI_DECRYPT_SNIPPET, value);
209
220
  }
210
221
  function mergeConfig(base, override) {
211
222
  return {
@@ -352,9 +363,15 @@ function writeSetupConfig(target, input) {
352
363
  const protectedShieldKey = input.shieldKey && process.platform === 'win32'
353
364
  ? protectShieldKeyForCurrentWindowsUser(input.shieldKey)
354
365
  : undefined;
355
- if (protectedShieldKey) {
366
+ // Roundtrip-verify before trusting DPAPI: encryption is cmdlet-only and
367
+ // succeeds even where decryption is blocked (Constrained Language Mode /
368
+ // EDR), which used to delete the plaintext key on a machine that could
369
+ // never read the encrypted one back — every hook then 401'd fail-closed.
370
+ const dpapiVerified = !!protectedShieldKey
371
+ && unprotectShieldKeyForCurrentWindowsUser(protectedShieldKey) === input.shieldKey;
372
+ if (protectedShieldKey && dpapiVerified) {
356
373
  setTopLevel('shieldKeyDpapi', protectedShieldKey);
357
- // Remove legacy plaintext key on a successful Windows DPAPI migration.
374
+ // Remove legacy plaintext key on a VERIFIED Windows DPAPI migration.
358
375
  const plaintextIndex = lines.findIndex(line => /^shieldKey:/.test(line.trim()));
359
376
  if (plaintextIndex >= 0)
360
377
  lines.splice(plaintextIndex, 1);
package/dist/version.json CHANGED
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "1.21.31"
2
+ "version": "1.21.32"
3
3
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fullcourtdefense-cli",
3
- "version": "1.21.31",
3
+ "version": "1.21.32",
4
4
  "description": "Full Court Defense CLI — security scanning for AI agents from your terminal",
5
5
  "main": "dist/index.js",
6
6
  "bin": {