node-opcua-pki 6.22.0 → 6.22.1

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/index.d.mts CHANGED
@@ -364,6 +364,8 @@ declare enum VerificationStatus {
364
364
  }
365
365
  declare function coerceCertificateChain(certificate: Certificate | Certificate[]): Certificate[];
366
366
  declare function makeFingerprint(certificate: Certificate | Certificate[] | CertificateRevocationList): string;
367
+ declare function toFilenameLabel(commonName: string): string;
368
+ declare function safeStoreJoin(folder: string, filename: string): string;
367
369
  /**
368
370
  * Check if the provided certificate acts as an issuer (CA)
369
371
  * @param certificate - the DER-encoded certificate
@@ -998,4 +1000,4 @@ declare function convertPFXtoPEM(pfxFile: Filename, pemFile: Filename, passphras
998
1000
  */
999
1001
  declare function dumpPFX(pfxFile: Filename, passphrase?: string): Promise<string>;
1000
1002
 
1001
- export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint };
1003
+ export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint, safeStoreJoin, toFilenameLabel };
package/dist/index.d.ts CHANGED
@@ -364,6 +364,8 @@ declare enum VerificationStatus {
364
364
  }
365
365
  declare function coerceCertificateChain(certificate: Certificate | Certificate[]): Certificate[];
366
366
  declare function makeFingerprint(certificate: Certificate | Certificate[] | CertificateRevocationList): string;
367
+ declare function toFilenameLabel(commonName: string): string;
368
+ declare function safeStoreJoin(folder: string, filename: string): string;
367
369
  /**
368
370
  * Check if the provided certificate acts as an issuer (CA)
369
371
  * @param certificate - the DER-encoded certificate
@@ -998,4 +1000,4 @@ declare function convertPFXtoPEM(pfxFile: Filename, pemFile: Filename, passphras
998
1000
  */
999
1001
  declare function dumpPFX(pfxFile: Filename, passphrase?: string): Promise<string>;
1000
1002
 
1001
- export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint };
1003
+ export { type AddCertificateValidationOptions, CaBackend, CertificateAuthority, CertificateAuthorityCore, CertificateAuthorityCoreOptions, type CertificateAuthorityOptions, CertificateManager, type CertificateManagerEvents, type CertificateManagerOptions, CertificateManagerState, type CertificateStore, type ChainCompletionResult, ChainCompletionStatus, type CreatePFXOptions, CreateSelfSignCertificateParam, type CreateSelfSignCertificateParam1, type CrlStore, type ExtractPFXOptions, type ExtractPFXResult, Filename, KeySize, OpenSslCaBackend, Params, PrivateKeyPassphrase, PrivateKeyProvider, ProcessAltNamesParam, VerificationStatus, type VerifyCertificateOptions, coerceCertificateChain, convertPFXtoPEM, createPFX, dumpPFX, extractAllFromPFX, extractCACertificatesFromPFX, extractCertificateFromPFX, extractPrivateKeyFromPFX, findIssuerCertificateInChain, install_prerequisite, isIntermediateIssuer, isIssuer, isRootIssuer, makeFingerprint, safeStoreJoin, toFilenameLabel };
package/dist/index.js CHANGED
@@ -59,7 +59,9 @@ __export(lib_exports, {
59
59
  isRootIssuer: () => isRootIssuer,
60
60
  makeFingerprint: () => makeFingerprint,
61
61
  quote: () => quote,
62
- resolvePrivateKeyPassphrase: () => resolvePrivateKeyPassphrase
62
+ resolvePrivateKeyPassphrase: () => resolvePrivateKeyPassphrase,
63
+ safeStoreJoin: () => safeStoreJoin,
64
+ toFilenameLabel: () => toFilenameLabel
63
65
  });
64
66
  module.exports = __toCommonJS(lib_exports);
65
67
  var import_node_opcua_crypto10 = require("node-opcua-crypto");
@@ -1065,8 +1067,32 @@ function passinArg(passphrase = "") {
1065
1067
  function renderForDisplay(file, args) {
1066
1068
  return [file, ...args].map((a) => a === "" || /[\s"'`$\\]/.test(a) ? JSON.stringify(a) : a).join(" ");
1067
1069
  }
1070
+ function hasUnsafeArgChar(value) {
1071
+ for (const ch of value) {
1072
+ const code = ch.codePointAt(0);
1073
+ if (code < 32 || code === 127) {
1074
+ return true;
1075
+ }
1076
+ if (code < 128) {
1077
+ continue;
1078
+ }
1079
+ if (/[\p{L}\p{N}\p{M}]/u.test(ch)) {
1080
+ continue;
1081
+ }
1082
+ return true;
1083
+ }
1084
+ return false;
1085
+ }
1086
+ function checkArgs(file, args) {
1087
+ for (const value of [file, ...args]) {
1088
+ if (typeof value === "string" && hasUnsafeArgChar(value)) {
1089
+ throw new Error(`execute_openssl: refusing argument with an unexpected character: ${JSON.stringify(value)}`);
1090
+ }
1091
+ }
1092
+ }
1068
1093
  async function execute2(file, args, options) {
1069
1094
  const from = new Error();
1095
+ checkArgs(file, args);
1070
1096
  options.cwd = options.cwd || process.cwd();
1071
1097
  if (!g_config.silent) {
1072
1098
  warningLog(import_chalk4.default.cyan(" CWD "), options.cwd);
@@ -3001,18 +3027,31 @@ function makeFingerprint(certificate) {
3001
3027
  function short(stringToShorten) {
3002
3028
  return stringToShorten.substring(0, 10);
3003
3029
  }
3004
- var forbiddenChars = /[\x00-\x1F<>:"/\\|?*]/g;
3030
+ var disallowedInLabel = /[^A-Za-z0-9._ -]/g;
3031
+ function toFilenameLabel(commonName) {
3032
+ const label = (commonName || "").normalize("NFKC").replace(disallowedInLabel, "_").replace(/[^\x20-\x7E]/g, "").slice(0, 64).trim();
3033
+ return label || "certificate";
3034
+ }
3005
3035
  function buildIdealCertificateName(certificate) {
3006
3036
  const chain = coerceCertificateChain(certificate);
3007
3037
  const fingerprint = makeFingerprint(chain);
3008
3038
  try {
3009
3039
  const commonName = (0, import_node_opcua_crypto8.exploreCertificate)(chain[0]).tbsCertificate.subject.commonName || "";
3010
- const sanitizedCommonName = commonName.replace(forbiddenChars, "_");
3040
+ const sanitizedCommonName = toFilenameLabel(commonName);
3011
3041
  return `${sanitizedCommonName}[${fingerprint}]`;
3012
3042
  } catch (_err) {
3013
3043
  return `invalid_certificate_[${fingerprint}]`;
3014
3044
  }
3015
3045
  }
3046
+ function safeStoreJoin(folder, filename) {
3047
+ const full = import_node_path8.default.resolve(folder, filename);
3048
+ const base = import_node_path8.default.resolve(folder);
3049
+ if (full !== base && !full.startsWith(base + import_node_path8.default.sep)) {
3050
+ warningLog("refusing to write certificate outside its store folder:", filename);
3051
+ throw new Error(`certificate filename escapes its store folder: ${filename}`);
3052
+ }
3053
+ return full;
3054
+ }
3016
3055
  function findMatchingIssuerKey(entries, wantedIssuerKey) {
3017
3056
  return entries.filter((entry) => {
3018
3057
  const info = getOrComputeInfo(entry);
@@ -3588,7 +3627,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
3588
3627
  } catch (_err) {
3589
3628
  return "BadCertificateInvalid";
3590
3629
  }
3591
- const filename = import_node_path8.default.join(this.rejectedFolder, `${buildIdealCertificateName(leafCertificate)}.pem`);
3630
+ const filename = safeStoreJoin(this.rejectedFolder, `${buildIdealCertificateName(leafCertificate)}.pem`);
3592
3631
  debugLog("certificate has never been seen before and is now rejected (untrusted) ", filename);
3593
3632
  await fsWriteFile(filename, (0, import_node_opcua_crypto8.toPem)(chain, "CERTIFICATE"));
3594
3633
  this.#thumbs.rejected.set(fingerprint, { certificate: leafCertificate, filename });
@@ -3996,7 +4035,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
3996
4035
  if (this.#thumbs.issuers.certs.has(fingerprint)) {
3997
4036
  return "Good" /* Good */;
3998
4037
  }
3999
- const filename = import_node_path8.default.join(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);
4038
+ const filename = safeStoreJoin(this.issuersCertFolder, `issuer_${buildIdealCertificateName(certificate)}.pem`);
4000
4039
  await import_node_fs11.default.promises.writeFile(filename, pemCertificate, "ascii");
4001
4040
  this.#thumbs.issuers.certs.set(fingerprint, { certificate, filename });
4002
4041
  if (addInTrustList) {
@@ -4455,7 +4494,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
4455
4494
  let status = await this.#checkRejectedOrTrusted(certificate);
4456
4495
  if (status === "unknown") {
4457
4496
  const pem = (0, import_node_opcua_crypto8.toPem)(chain, "CERTIFICATE");
4458
- const filename = import_node_path8.default.join(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
4497
+ const filename = safeStoreJoin(this.rejectedFolder, `${buildIdealCertificateName(certificate)}.pem`);
4459
4498
  await import_node_fs11.default.promises.writeFile(filename, pem);
4460
4499
  this.#thumbs.rejected.set(fingerprint, { certificate, filename });
4461
4500
  status = "rejected";
@@ -4472,7 +4511,7 @@ var CertificateManager = class _CertificateManager extends import_node_events.Ev
4472
4511
  throw new Error(`#moveCertificate: certificate ${fingerprint.substring(0, 10)} not found in ${status} index`);
4473
4512
  }
4474
4513
  const destFolder = newStatus === "trusted" ? this.trustedFolder : this.rejectedFolder;
4475
- const certificateDest = import_node_path8.default.join(destFolder, import_node_path8.default.basename(srcEntry.filename));
4514
+ const certificateDest = safeStoreJoin(destFolder, import_node_path8.default.basename(srcEntry.filename));
4476
4515
  debugLog("#moveCertificate", fingerprint.substring(0, 10), "old name", srcEntry.filename);
4477
4516
  debugLog("#moveCertificate", fingerprint.substring(0, 10), "new name", certificateDest);
4478
4517
  await import_node_fs11.default.promises.rename(srcEntry.filename, certificateDest);
@@ -4963,6 +5002,8 @@ async function dumpPFX(pfxFile, passphrase = "") {
4963
5002
  isRootIssuer,
4964
5003
  makeFingerprint,
4965
5004
  quote,
4966
- resolvePrivateKeyPassphrase
5005
+ resolvePrivateKeyPassphrase,
5006
+ safeStoreJoin,
5007
+ toFilenameLabel
4967
5008
  });
4968
5009
  //# sourceMappingURL=index.js.map