Skip to content

Commit 36449ca

Browse files
committed
Support self signed certificates in GitHub Copilot
1 parent 8efa24d commit 36449ca

File tree

5 files changed

+78
-4
lines changed

5 files changed

+78
-4
lines changed

Core/Sources/HostApp/AccountSettings/GitHubCopilotView.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ struct GitHubCopilotView: View {
2222
@AppStorage(\.gitHubCopilotEnterpriseURI) var gitHubCopilotEnterpriseURI
2323
@AppStorage(\.disableGitHubCopilotSettingsAutoRefreshOnAppear)
2424
var disableGitHubCopilotSettingsAutoRefreshOnAppear
25+
@AppStorage(\.gitHubCopilotLoadKeyChainCertificates)
26+
var gitHubCopilotLoadKeyChainCertificates
2527
init() {}
2628
}
2729

@@ -196,6 +198,10 @@ struct GitHubCopilotView: View {
196198
.foregroundColor(.secondary)
197199
.font(.callout)
198200
.dynamicHeightTextInFormWorkaround()
201+
202+
Toggle(isOn: $settings.gitHubCopilotLoadKeyChainCertificates) {
203+
Text("Load certificates in keychain")
204+
}
199205
}
200206
}
201207

Tool/Package.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,8 @@ let package = Package(
316316
"BuiltinExtension",
317317
.product(name: "LanguageServerProtocol", package: "LanguageServerProtocol"),
318318
.product(name: "CopilotForXcodeKit", package: "CopilotForXcodeKit"),
319-
]
319+
],
320+
resources: [.copy("Resources/load-self-signed-cert.js")]
320321
),
321322
.testTarget(
322323
name: "GitHubCopilotServiceTests",

Tool/Sources/GitHubCopilotService/LanguageServer/GitHubCopilotService.swift

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,14 @@ protocol GitHubCopilotLSP {
4242
enum GitHubCopilotError: Error, LocalizedError {
4343
case languageServerNotInstalled
4444
case languageServerError(ServerError)
45+
case failedToInstallStartScript
4546

4647
var errorDescription: String? {
4748
switch self {
4849
case .languageServerNotInstalled:
4950
return "Language server is not installed."
51+
case .failedToInstallStartScript:
52+
return "Failed to install start script."
5053
case let .languageServerError(error):
5154
switch error {
5255
case let .handlerUnavailable(handler):
@@ -109,12 +112,32 @@ public class GitHubCopilotBaseService {
109112
throw GitHubCopilotError.languageServerNotInstalled
110113
}
111114

115+
let indexJSURL: URL = try {
116+
if UserDefaults.shared.value(for: \.gitHubCopilotLoadKeyChainCertificates) {
117+
let url = urls.executableURL.appendingPathComponent("load-self-signed-cert.js")
118+
if !FileManager.default.fileExists(atPath: url.path) {
119+
let file = Bundle.module.url(
120+
forResource: "load-self-signed-cert",
121+
withExtension: "js"
122+
)!
123+
do {
124+
try FileManager.default.copyItem(at: file, to: url)
125+
} catch {
126+
throw GitHubCopilotError.failedToInstallStartScript
127+
}
128+
}
129+
return url
130+
} else {
131+
return agentJSURL
132+
}
133+
}()
134+
112135
switch runner {
113136
case .bash:
114137
let nodePath = UserDefaults.shared.value(for: \.nodePath)
115138
let command = [
116139
nodePath.isEmpty ? "node" : nodePath,
117-
"\"\(agentJSURL.path)\"",
140+
"\"\(indexJSURL.path)\"",
118141
"--stdio",
119142
].joined(separator: " ")
120143
executionParams = Process.ExecutionParameters(
@@ -128,7 +151,7 @@ public class GitHubCopilotBaseService {
128151
let nodePath = UserDefaults.shared.value(for: \.nodePath)
129152
let command = [
130153
nodePath.isEmpty ? "node" : nodePath,
131-
"\"\(agentJSURL.path)\"",
154+
"\"\(indexJSURL.path)\"",
132155
"--stdio",
133156
].joined(separator: " ")
134157
executionParams = Process.ExecutionParameters(
@@ -146,7 +169,7 @@ public class GitHubCopilotBaseService {
146169
path: "/usr/bin/env",
147170
arguments: [
148171
nodePath.isEmpty ? "node" : nodePath,
149-
agentJSURL.path,
172+
indexJSURL.path,
150173
"--stdio",
151174
],
152175
environment: [
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
function initialize() {
2+
if (process.platform !== "darwin") {
3+
return;
4+
}
5+
6+
const splitPattern = /(?=-----BEGIN\sCERTIFICATE-----)/g;
7+
const systemRootCertsPath =
8+
"/System/Library/Keychains/SystemRootCertificates.keychain";
9+
const args = ["find-certificate", "-a", "-p"];
10+
11+
const childProcess = require("child_process");
12+
const allTrusted = childProcess
13+
.spawnSync("/usr/bin/security", args)
14+
.stdout.toString()
15+
.split(splitPattern);
16+
17+
const allRoot = childProcess
18+
.spawnSync("/usr/bin/security", args.concat(systemRootCertsPath))
19+
.stdout.toString()
20+
.split(splitPattern);
21+
const all = allTrusted.concat(allRoot);
22+
23+
const tls = require("tls");
24+
const origCreateSecureContext = tls.createSecureContext;
25+
tls.createSecureContext = (options) => {
26+
const ctx = origCreateSecureContext(options);
27+
all.filter(duplicated).forEach((cert) => {
28+
ctx.context.addCACert(cert.trim());
29+
});
30+
return ctx;
31+
};
32+
}
33+
34+
function duplicated(cert, index, arr) {
35+
return arr.indexOf(cert) === index;
36+
}
37+
38+
initialize();
39+
40+
require("./copilot/dist/agent.js");

Tool/Sources/Preferences/Keys.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,10 @@ public extension UserDefaultPreferenceKeys {
187187
var runNodeWith: PreferenceKey<NodeRunner> {
188188
.init(defaultValue: .env, key: "RunNodeWith")
189189
}
190+
191+
var gitHubCopilotLoadKeyChainCertificates: PreferenceKey<Bool> {
192+
.init(defaultValue: false, key: "GitHubCopilotLoadKeyChainCertificates")
193+
}
190194
}
191195

192196
// MARK: - Codeium Settings

0 commit comments

Comments
 (0)