Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3e18934

Browse files
committed
Add binary signature verification
1 parentac10af8 commit3e18934

File tree

9 files changed

+363
-9
lines changed

9 files changed

+363
-9
lines changed

‎CHANGELOG.md‎

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,20 @@
22

33
##Unreleased
44

5+
###Changed
6+
7+
- Coder output panel enhancements: All log entries now include timestamps, and you
8+
can filter messages by log level in the panel.
9+
10+
###Added
11+
512
- Update`/openDevContainer` to support all dev container features when hostPath
613
and configFile are provided.
714
- Add`coder.disableUpdateNotifications` setting to disable workspace template
815
update notifications.
9-
- Coder output panel enhancements: All log entries now include timestamps, and you
10-
can filter messages by log level in the panel.
16+
- Add binary signature verification. This can be disabled with
17+
`coder.disableSignatureVerification` if you purposefully run a binary that is
18+
not signed by Coder (for example a binary you built yourself).
1119

1220
##[v1.9.2](https://github.com/coder/vscode-coder/releases/tag/v1.9.2) 2025-06-25
1321

‎package.json‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@
114114
"markdownDescription":"Disable notifications when workspace template updates are available.",
115115
"type":"boolean",
116116
"default":false
117+
},
118+
"coder.disableSignatureVerification": {
119+
"markdownDescription":"Disable Coder CLI signature verification, which can be useful if you run an unsigned fork of the binary.",
120+
"type":"boolean",
121+
"default":false
117122
}
118123
}
119124
},
@@ -289,6 +294,7 @@
289294
"jsonc-parser":"^3.3.1",
290295
"memfs":"^4.17.1",
291296
"node-forge":"^1.3.1",
297+
"openpgp":"^6.2.0",
292298
"pretty-bytes":"^6.1.1",
293299
"proxy-agent":"^6.4.0",
294300
"semver":"^7.7.1",

‎pgp-public.key‎

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
-----BEGIN PGP PUBLIC KEY BLOCK-----
2+
3+
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
98+
=dLmT
99+
-----END PGP PUBLIC KEY BLOCK-----

‎src/api-helper.ts‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { Workspace, WorkspaceAgent } from "coder/site/src/api/typesGenerated";
33
import{ErrorEvent}from"eventsource";
44
import{z}from"zod";
55

6-
exportfunctionerrToStr(error:unknown,def:string){
6+
exportfunctionerrToStr(
7+
error:unknown,
8+
def:string="No error message provided",
9+
){
710
if(errorinstanceofError&&error.message){
811
returnerror.message;
912
}elseif(isApiError(error)){

‎src/cliManager.ts‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ export type RemovalResult = { fileName: string; error: unknown };
7878

7979
/**
8080
* Remove binaries in the same directory as the specified path that have a
81-
* .old-* or .temp-* extension. Return a list offilesand the errors trying to
82-
* remove them, when applicable.
81+
* .old-* or .temp-* extension along with signatures (filesending in .asc).
82+
*Return a list of files and the errors trying toremove them, when applicable.
8383
*/
8484
exportasyncfunctionrmOld(binPath:string):Promise<RemovalResult[]>{
8585
constbinDir=path.dirname(binPath);
@@ -88,7 +88,11 @@ export async function rmOld(binPath: string): Promise<RemovalResult[]> {
8888
constresults:RemovalResult[]=[];
8989
for(constfileoffiles){
9090
constfileName=path.basename(file);
91-
if(fileName.includes(".old-")||fileName.includes(".temp-")){
91+
if(
92+
fileName.includes(".old-")||
93+
fileName.includes(".temp-")||
94+
fileName.endsWith(".asc")
95+
){
9296
try{
9397
awaitfs.rm(path.join(binDir,file),{force:true});
9498
results.push({ fileName,error:undefined});

‎src/extension.ts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise<void> {
4949

5050
constoutput=vscode.window.createOutputChannel("Coder",{log:true});
5151
conststorage=newStorage(
52+
vscodeProposed,
5253
output,
5354
ctx.globalState,
5455
ctx.secrets,

‎src/pgp.ts‎

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
import{createReadStream,promisesasfs}from"fs";
2+
import*asopenpgpfrom"openpgp";
3+
import*aspathfrom"path";
4+
import{Readable}from"stream";
5+
import*asvscodefrom"vscode";
6+
import{errToStr}from"./api-helper";
7+
8+
exporttypeKey=openpgp.Key;
9+
10+
exportenumVerificationErrorCode{
11+
/* The signature does not match. */
12+
Invalid="Invalid",
13+
/* Failed to read the signature or the file to verify. */
14+
Read="Read",
15+
}
16+
17+
exportclassVerificationErrorextendsError{
18+
constructor(
19+
publicreadonlycode:VerificationErrorCode,
20+
message:string,
21+
){
22+
super(message);
23+
}
24+
25+
summary():string{
26+
switch(this.code){
27+
caseVerificationErrorCode.Invalid:
28+
return"Signature does not match";
29+
default:
30+
return"Failed to read signature";
31+
}
32+
}
33+
}
34+
35+
/**
36+
* Return the public keys bundled with the plugin.
37+
*/
38+
exportasyncfunctionreadPublicKeys(
39+
logger:vscode.LogOutputChannel,
40+
):Promise<Key[]>{
41+
constkeyFile=path.join(__dirname,"../pgp-public.key");
42+
logger.info("Reading public key",keyFile);
43+
constarmoredKeys=awaitfs.readFile(keyFile,"utf8");
44+
returnopenpgp.readKeys({ armoredKeys});
45+
}
46+
47+
/**
48+
* Given public keys, a path to a file to verify, and a path to a detached
49+
* signature, verify the file's signature. Return true if valid, otherwise
50+
* return VerificationError.
51+
*/
52+
exportasyncfunctionverifySignature(
53+
logger:vscode.LogOutputChannel,
54+
publicKeys:openpgp.Key[],
55+
cliPath:string,
56+
signaturePath:string,
57+
):Promise<true|VerificationError>{
58+
try{
59+
logger.info("Reading signature",signaturePath);
60+
constarmoredSignature=awaitfs.readFile(signaturePath,"utf8");
61+
constsignature=awaitopenpgp.readSignature({ armoredSignature});
62+
63+
logger.info("Verifying signature of",cliPath);
64+
constmessage=awaitopenpgp.createMessage({
65+
// openpgpjs only accepts web readable streams.
66+
binary:Readable.toWeb(createReadStream(cliPath)),
67+
});
68+
constverificationResult=awaitopenpgp.verify({
69+
message,
70+
signature,
71+
verificationKeys:publicKeys,
72+
});
73+
forawait(const_ofverificationResult.data){
74+
// The docs indicate this data must be consumed; it triggers the
75+
// verification of the data.
76+
}
77+
try{
78+
const{ verified}=verificationResult.signatures[0];
79+
awaitverified;// Throws on invalid signature.
80+
logger.info("Binary signature matches");
81+
}catch(e){
82+
consterror=`Unable to verify the authenticity of the binary:${errToStr(e)}. The binary may have been tampered with.`;
83+
logger.warn(error);
84+
returnnewVerificationError(VerificationErrorCode.Invalid,error);
85+
}
86+
}catch(e){
87+
consterror=`Failed to read signature or binary:${errToStr(e)}.`;
88+
logger.warn(error);
89+
returnnewVerificationError(VerificationErrorCode.Read,error);
90+
}
91+
returntrue;
92+
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp