forked from webosbrew/webos-homebrew-channel
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This should work on all webOS versions. It loads certificates from the Mozilla CA cert bundle. Users may add additional certs by placing PEM-encoded files in the "certs" directory.
- Loading branch information
1 parent
f19064a
commit 10ed3ba
Showing
6 changed files
with
3,792 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* fetch-wrapper.ts | ||
* | ||
* Makes fetch() trust our CA certs. Certs loaded on first use. | ||
* | ||
* This is part of webOS Homebrew Channel | ||
* https://github.com/webosbrew/webos-homebrew-channel | ||
* Copyright 2024 throwaway96. | ||
*/ | ||
|
||
import http from 'http'; | ||
import https from 'https'; | ||
|
||
import { loadCertDir } from './load-certs'; | ||
|
||
import fetch from 'node-fetch'; | ||
|
||
function createAgent(): https.Agent { | ||
const certsDir = __dirname + '/certs'; | ||
const certs = loadCertDir(certsDir, { | ||
logLevel: 0, | ||
}); | ||
|
||
return new https.Agent({ ca: certs }); | ||
} | ||
|
||
let defaultHttpsAgent: https.Agent | null = null; | ||
|
||
export function fetchWrapper(url: fetch.RequestInfo, init?: fetch.RequestInit): Promise<fetch.Response> { | ||
if (defaultHttpsAgent === null) { | ||
defaultHttpsAgent = createAgent(); | ||
} | ||
|
||
if (typeof init === 'undefined') { | ||
init = {}; | ||
} | ||
|
||
init.agent = (parsedURL: URL): http.RequestOptions['agent'] => { | ||
if (parsedURL.protocol == 'http:') { | ||
return http.globalAgent; | ||
} else { | ||
return defaultHttpsAgent as https.Agent; | ||
} | ||
}; | ||
|
||
return fetch(url, init); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
/** | ||
* Load PEM certificates from a directory. | ||
* | ||
* @param {string} dir Cert directory. | ||
* @param {{ logLevel: number }} options Options. | ||
* @returns {string[]} The certificates. | ||
*/ | ||
export function loadCertDir( | ||
dir: string, | ||
options: { | ||
logLevel: number; | ||
}, | ||
): string[]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/* | ||
* load-certs.js | ||
* | ||
* Loads PEM certificates from a directory. Not recursive. | ||
* | ||
* This is part of webOS Homebrew Channel | ||
* https://github.com/webosbrew/webos-homebrew-channel | ||
* Copyright 2024 throwaway96 | ||
*/ | ||
|
||
const fs = require('fs'); /* eslint-disable-line @typescript-eslint/no-var-requires */ | ||
const path = require('path'); /* eslint-disable-line @typescript-eslint/no-var-requires */ | ||
|
||
function createLogger(logLevel) { | ||
if (typeof logLevel !== 'number') { | ||
throw new Error('invalid argument type (logLevel): ' + typeof logLevel); | ||
} | ||
|
||
function _ignore() {} | ||
|
||
return { | ||
debug: logLevel <= 0 ? console.log : _ignore, | ||
verbose: logLevel <= 1 ? console.log : _ignore, | ||
info: logLevel <= 2 ? console.log : _ignore, | ||
warn: logLevel <= 3 ? console.warn : _ignore, | ||
error: logLevel <= 4 ? console.error : _ignore, | ||
}; | ||
} | ||
|
||
const pemRegex = /-----BEGIN CERTIFICATE-----\n[^]+?\n-----END CERTIFICATE-----/g; | ||
|
||
function readPem(file) { | ||
var results, content; | ||
|
||
if (typeof file !== 'string') { | ||
throw new Error('invalid argument type: ' + typeof file); | ||
} | ||
|
||
const newCAs = []; | ||
|
||
try { | ||
content = fs.readFileSync(file, { encoding: 'ascii' }).trim().replace(/\r\n/g, '\n'); | ||
} catch (err) { | ||
throw new Error('error in read(' + file + '): ' + err.message, { | ||
cause: err, | ||
}); | ||
} | ||
|
||
if ((results = content.match(pemRegex)) === null) { | ||
throw new Error('could not parse PEM certificate(s)'); | ||
} | ||
|
||
results.forEach(function pemIterate(match) { | ||
newCAs.push(match.trim()); | ||
}); | ||
|
||
return newCAs; | ||
} | ||
|
||
/** | ||
* Load PEM certificates from a directory. | ||
* | ||
* @param {string} dir Cert directory. | ||
* @param {{ logLevel: number }} options Options. | ||
* @returns {string[]} The certificates. | ||
*/ | ||
function loadCertDir(dir, options) { | ||
const rootCerts = []; | ||
var files, dirStat, logLevel; | ||
|
||
if (typeof dir !== 'string') { | ||
throw new Error('invalid argument type (dir): ' + typeof dir); | ||
} | ||
|
||
if (typeof options === 'undefined') { | ||
options = {}; | ||
} else if (typeof options !== 'object') { | ||
throw new Error('invalid argument type (options): ' + typeof options); | ||
} | ||
|
||
if (typeof options.logLevel === 'undefined') { | ||
logLevel = 3; /* 3 -> warnings */ | ||
} else if (typeof options.logLevel === 'number') { | ||
logLevel = options.logLevel; | ||
} else { | ||
throw new Error('invalid argument type (options.logLevel): ' + typeof options.logLevel); | ||
} | ||
|
||
const log = createLogger(logLevel); | ||
|
||
try { | ||
dirStat = fs.statSync(dir); | ||
} catch (err) { | ||
if (err.code === 'ENOENT') { | ||
throw new Error('directory does not exist: ' + dir, { cause: err }); | ||
} else { | ||
throw new Error('error in stat(' + dir + '): ' + err.message, { | ||
cause: err, | ||
}); | ||
} | ||
} | ||
|
||
if (!dirStat.isDirectory()) { | ||
throw new Error('not a directory: ' + dir); | ||
} | ||
|
||
try { | ||
files = fs.readdirSync(dir); | ||
} catch (err) { | ||
throw new Error('error in readdir(' + dir + '): ' + err.message, { | ||
cause: err, | ||
}); | ||
} | ||
|
||
files | ||
.map(function resolvePath(filename) { | ||
return path.resolve(dir, filename); | ||
}) | ||
.forEach(function processFile(file) { | ||
var stat; | ||
|
||
try { | ||
stat = fs.statSync(file); | ||
} catch (err) { | ||
log.verbose('error in stat(' + file + '): ' + err.message); | ||
return; | ||
} | ||
|
||
if (stat.isFile()) { | ||
try { | ||
readPem(file).forEach(function certIterate(cert) { | ||
if (rootCerts.indexOf(cert) !== -1) { | ||
log.debug('duplicate cert from ' + file); | ||
} else { | ||
rootCerts.push(cert); | ||
} | ||
}); | ||
} catch (err) { | ||
log.verbose('failed to read cert file ' + file + ': ' + err.message); | ||
} | ||
} | ||
}); | ||
|
||
return rootCerts; | ||
} | ||
|
||
module.exports.loadCertDir = loadCertDir; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters