Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

mac-ca

Package Overview
Dependencies
Maintainers
1
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mac-ca - npm Package Compare versions

Comparing version
2.0.0
to
2.0.2
+6
dist/asn1.d.ts
export type asn1 = {
serial: any;
issuer: any;
valid: any;
subject: any;
};
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
import * as forge from 'node-forge';
import { asn1 } from './asn1';
export declare enum Format {
der = "der",
pem = "pem",
txt = "txt",
asn1 = "asn1",
x509 = "x509",
fingerprint = "fingerprint"
}
export type ConvertResult = string | forge.util.ByteStringBuffer | asn1 | forge.pki.Certificate;
export declare function convert(pem: string, format: Format.pem | Format.txt | Format.fingerprint): string;
export declare function convert(pem: string, format: Format.der): forge.util.ByteStringBuffer;
export declare function convert(pem: string, format: Format.asn1): asn1;
export declare function convert(pem: string, format: Format.x509): forge.pki.Certificate;
export declare function convert(pem: string, format: Format): string | forge.util.ByteStringBuffer | asn1 | forge.pki.Certificate;
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.convert = exports.Format = void 0;
var forge = require("node-forge");
var Format;
(function (Format) {
Format["der"] = "der";
Format["pem"] = "pem";
Format["txt"] = "txt";
Format["asn1"] = "asn1";
Format["x509"] = "x509";
Format["fingerprint"] = "fingerprint";
})(Format = exports.Format || (exports.Format = {}));
;
function myASN(pem) {
var der = forge.pki.pemToDer(pem);
var asn1 = forge.asn1;
// @ts-ignore
var crt = asn1.fromDer(der.data.toString('binary')).value[0].value;
var serial = crt[0];
var hasSerial = serial.tagClass === asn1.Class.CONTEXT_SPECIFIC &&
serial.type === 0 &&
serial.constructed;
var slicedCrt = crt.slice(hasSerial);
return {
serial: slicedCrt[0],
issuer: slicedCrt[2],
valid: slicedCrt[3],
subject: slicedCrt[4],
};
}
function txtFormat(pem) {
var crt = myASN(pem);
var subject = crt.subject.value
// @ts-ignore
.map(function (rdn) { return rdn.value[0].value[1].value; })
.join('/');
var valid = crt.valid.value
// @ts-ignore
.map(function (date) { return date.value; })
.join(' - ');
return [
"Subject\t".concat(subject),
"Valid\t".concat(valid),
String(pem),
].join('\n');
}
function convert(pem, format) {
switch (format) {
case Format.der:
return forge.pki.pemToDer(pem);
case Format.pem:
return pem;
case Format.txt:
return txtFormat(pem);
case Format.asn1:
return myASN(pem);
case Format.fingerprint:
var md = forge.md.sha1.create();
var der = convert(pem, Format.der);
md.update(der.getBytes());
return md.digest().toHex();
case Format.x509:
return forge.pki.certificateFromPem(pem);
default:
throw new Error("unknown format ".concat(format));
}
}
exports.convert = convert;
;
import * as forge from 'node-forge';
import { Format, convert } from './formatter';
import { asn1 } from './asn1';
type GetParams = {
/**
* The keychain to search.
* Defaults to "all".
*/
keychain?: 'all' | 'current' | 'SystemRootCertificates';
/**
* remove duplicated certificates
* Defaults to true
*/
unique?: Boolean;
/**
* Exclude node.js bundled root certificates.
* Defaults to true.
*/
excludeBundled?: Boolean;
/**
* The format to retrieve them
* Defaults to "pem".
*/
format?: Format;
};
export declare function get(params?: GetParams & {
format: Format.pem | Format.txt | Format.fingerprint;
}): string[];
export declare function get(params?: GetParams & {
format: Format.der;
}): forge.util.ByteStringBuffer[];
export declare function get(params?: GetParams & {
format: Format.asn1;
}): asn1[];
export declare function get(params?: GetParams & {
format: Format.x509;
}): forge.pki.Certificate[];
export declare function get(): string[];
type AddToGAType = Exclude<GetParams, 'format'>;
export declare const addToGlobalAgent: (params?: AddToGAType) => void;
export { Format, convert };
+4
-1
{
"name": "mac-ca",
"version": "2.0.0",
"version": "2.0.2",
"description": "Get Mac OS Root certificates",
"main": "dist/index.js",
"files": [
"dist/**/*"
],
"repository": {

@@ -7,0 +10,0 @@ "type": "git",

+19
-10

@@ -1,6 +0,4 @@

This module is similar to [win-ca][] but for Mac OS. In fact I have copied most of its documentation and I have tried to maintain api compatibility.
# mac-ca
Get MacOS System Root certificates for [Node.js][].
Get MacOS System certificates into [Node.js][].

@@ -15,4 +13,6 @@ ## Rationale

This package is intended to fetch Root CAs from MacOS "SystemRootCertificates.keychain" and make them available to [Node.js] application with minimal efforts.
This is most of the time ok, but there are cases where you might for example want to use TLS in development with self signed certificates for which you have a root certificate in your local store. Eg: [mkcert](https://github.com/FiloSottile/mkcert)
This package is intended to fetch Root CAs from MacOS "SystemRootCertificates.keychain" and the user's keychain into [Node.js].
### Advantages

@@ -27,11 +27,19 @@

Just say `npm install --save mac-ca` and then call `require('mac-ca').addToGlobalAgent();`.
Install with
```
npm install --save mac-ca
```
If called in other operative systems the method will do nothing.
And then use it as follows:
```
require('mac-ca').addToGlobalAgent();
```
If called in other operative systems the method will just do nothing.
## API
After `require('mac-ca').addToGlobalAgent()` MacOs' Root CAs are found, deduplicated and installed to `https.globalAgent.options.ca` so they are automatically used for all requests with Node.js' https module.
After `require('mac-ca').addToGlobalAgent()` MacOs' Root CAs are found, deduplicated and installed to `https.globalAgent.options.ca` so they are automatically used for all requests with Node.js' https module and any higher-level library like axios, node-fetch or request.
For use in other places, these certificates are also available via `.get()` method (in [node-forge][]'s format).
For use in other places, these certificates are also available via `.get()` method.

@@ -42,4 +50,4 @@ ```js

for (let crt of ca.get())
console.log(forge.pki.certificateToPem(crt))
for (let pem of ca.get())
console.log(pem)
```

@@ -76,2 +84,3 @@

[win-ca]: https://github.com/ukoloff/win-ca
[mkcert]: https://github.com/FiloSottile/mkcert
[node-forge]: https://github.com/digitalbazaar/forge

@@ -78,0 +87,0 @@ [OpenSSL::Win::Root]: https://github.com/ukoloff/openssl-win-root

{
"env": {
"es6": true,
"node": true,
"jest": true
},
"extends": "eslint:recommended",
"rules": {
"no-useless-escape": 1,
"no-console": 0,
"indent": [
"error",
2,
{
"SwitchCase": 1
}
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}
{
"editor.formatOnSave": true,
"eslint.format.enable": true,
"[javascript]": {
"editor.defaultFormatter": "dbaeumer.vscode-eslint"
}
}
export type asn1 = {
serial: any;
issuer: any;
valid: any;
subject: any;
};
import * as forge from 'node-forge';
import { asn1 } from './asn1';
export enum Format {
der = 'der',
pem = 'pem',
txt = 'txt',
asn1 = 'asn1',
x509 = 'x509',
fingerprint = 'fingerprint'
};
function myASN(pem: string): asn1 {
const der = forge.pki.pemToDer(pem);
const asn1 = forge.asn1;
// @ts-ignore
const crt = asn1.fromDer(der.data.toString('binary')).value[0].value;
const serial = crt[0];
const hasSerial =
serial.tagClass === asn1.Class.CONTEXT_SPECIFIC &&
serial.type === 0 &&
serial.constructed;
const slicedCrt = crt.slice(hasSerial);
return {
serial: slicedCrt[0],
issuer: slicedCrt[2],
valid: slicedCrt[3],
subject: slicedCrt[4],
};
}
function txtFormat(pem: string) {
const crt = myASN(pem);
const subject = crt.subject.value
// @ts-ignore
.map((rdn) => rdn.value[0].value[1].value)
.join('/');
const valid = crt.valid.value
// @ts-ignore
.map((date) => date.value)
.join(' - ');
return [
`Subject\t${subject}`,
`Valid\t${valid}`,
String(pem),
].join('\n');
}
export type ConvertResult = string | forge.util.ByteStringBuffer | asn1 | forge.pki.Certificate;
export function convert(pem: string, format: Format.pem | Format.txt | Format.fingerprint): string
export function convert(pem: string, format: Format.der): forge.util.ByteStringBuffer
export function convert(pem: string, format: Format.asn1): asn1
export function convert(pem: string, format: Format.x509): forge.pki.Certificate
export function convert(pem: string, format: Format): string | forge.util.ByteStringBuffer | asn1 | forge.pki.Certificate
export function convert(pem: string, format: Format): ConvertResult {
switch (format) {
case Format.der:
return forge.pki.pemToDer(pem);
case Format.pem:
return pem;
case Format.txt:
return txtFormat(pem);
case Format.asn1:
return myASN(pem);
case Format.fingerprint:
const md = forge.md.sha1.create();
const der = convert(pem, Format.der);
md.update(der.getBytes());
return md.digest().toHex();
case Format.x509:
return forge.pki.certificateFromPem(pem);
default:
throw new Error(`unknown format ${format}`);
}
};
import { globalAgent } from 'https';
import { rootCertificates } from 'tls';
import { spawnSync } from 'child_process';
import * as forge from 'node-forge';
import { Format, convert } from './formatter';
import { asn1 } from './asn1';
const isMac = process.platform === 'darwin';
type GetParams = {
/**
* The keychain to search.
* Defaults to "all".
*/
keychain?: 'all' | 'current' | 'SystemRootCertificates',
/**
* remove duplicated certificates
* Defaults to true
*/
unique?: Boolean,
/**
* Exclude node.js bundled root certificates.
* Defaults to true.
*/
excludeBundled?: Boolean,
/**
* The format to retrieve them
* Defaults to "pem".
*/
format?: Format
};
const getParamsDefaults: GetParams = {
keychain: 'all',
unique: true,
excludeBundled: true,
format: Format.pem
};
export function get(params?: GetParams & { format: Format.pem | Format.txt | Format.fingerprint }): string[]
export function get(params?: GetParams & { format: Format.der }): forge.util.ByteStringBuffer[]
export function get(params?: GetParams & { format: Format.asn1 }): asn1[]
export function get(params?: GetParams & { format: Format.x509 }): forge.pki.Certificate[]
export function get(): string[]
export function get(params: GetParams = getParamsDefaults): string[] | forge.util.ByteStringBuffer[] | asn1[] | forge.pki.Certificate[] {
if (!isMac) { return []; }
params = { ...getParamsDefaults, ...params };
const splitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g;
const args = ['find-certificate', '-a', '-p'];
let result: string[] = [];
if (params.keychain === 'all' || params.keychain === 'SystemRootCertificates') {
const systemRootCertsPath =
'/System/Library/Keychains/SystemRootCertificates.keychain';
const root = spawnSync('/usr/bin/security', args.concat(systemRootCertsPath))
.stdout.toString()
.split(splitPattern)
.map(c => c.trim());
result = [...result, ...root];
}
if (params.keychain === 'all' || params.keychain === 'SystemRootCertificates') {
const trusted = spawnSync('/usr/bin/security', args)
.stdout.toString()
.split(splitPattern)
.map(c => c.trim());
result = [...result, ...trusted];
}
if (params.unique || params.excludeBundled) {
const fingerprints = result.map(c => convert(c, Format.fingerprint));
const nodeFingerprints = params.excludeBundled ?
rootCertificates.map(c => convert(c, Format.fingerprint)) :
[];
result = result.filter((pem, index) => {
const fingerprint = fingerprints[index];
if (params.unique && index !== fingerprints.indexOf(fingerprint)) {
return false;
}
if (params.excludeBundled && nodeFingerprints.includes(fingerprint)) {
return false;
}
return true;
});
}
return result.map(c => convert(c, params.format)) as
string[] | forge.util.ByteStringBuffer[] | asn1[] | forge.pki.Certificate[];
};
const originalCA = globalAgent.options.ca;
type AddToGAType = Exclude<GetParams, 'format'>;
export const addToGlobalAgent = (params: AddToGAType = getParamsDefaults) => {
if (!isMac) { return; }
let cas: (string | Buffer)[];
if (!Array.isArray(originalCA)) {
cas = typeof originalCA !== 'undefined' ? [originalCA] : [];
} else {
cas = Array.from(originalCA);
}
get({ ...getParamsDefaults, ...params, format: Format.pem })
.forEach((cert) => cas.push(cert));
globalAgent.options.ca = cas;
};
export { Format, convert };
import { rootCertificates } from 'tls';
import * as assert from 'assert';
import { globalAgent } from 'https';
import * as macca from '../src';
describe('macca', () => {
it('should properly exclude node.js certs', () => {
const excludingNodejs = macca.get({ excludeBundled: true, format: macca.Format.fingerprint });
const nodejs = rootCertificates.map(c => macca.convert(c, macca.Format.fingerprint));
assert.ok(!excludingNodejs.find(c => nodejs.includes(c)));
});
it('should include some of the node.js certs when excludeBundled is false', () => {
const includingNodejs = macca.get({ excludeBundled: false, format: macca.Format.fingerprint });
const nodejs = rootCertificates.map(c => macca.convert(c, macca.Format.fingerprint));
assert.ok(includingNodejs.find(c => nodejs.includes(c)));
});
it('should install in the global agent', () => {
const certs = macca.get();
macca.addToGlobalAgent();
assert.ok(certs.every(c => globalAgent.options.ca?.includes(c)));
});
});
{
"compilerOptions": {
"outDir": "./dist",
"allowJs": true,
"target": "es5",
"declaration": true,
"esModuleInterop": false
},
"include": [
"./src/**/*"
]
}