Skip to content

Commit f0f00b0

Browse files
committed
feat: 敏感信息告警Linter
1 parent a279675 commit f0f00b0

File tree

6 files changed

+169
-67
lines changed

6 files changed

+169
-67
lines changed

package.json

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@
1515
],
1616
"activationEvents": [
1717
"onStartFinished",
18-
"onWebviewPanel:alicloud-api-webview"
18+
"onWebviewPanel:alicloud-api-webview",
19+
"onLanguage:javascript",
20+
"onLanguage:typescript",
21+
"onLanguage:go",
22+
"onLanguage:php",
23+
"onLanguage:python",
24+
"onLanguage:java"
1925
],
2026
"main": "./dist/extension",
2127
"contributes": {
@@ -24,6 +30,10 @@
2430
"command": "alicloud.api.autoImport",
2531
"title": "导入依赖"
2632
},
33+
{
34+
"command": "alicloud.api.akSecurityHelper",
35+
"title": "凭据的安全使用方案"
36+
},
2737
{
2838
"command": "alicloud.api.feedback",
2939
"title": "反馈",

src/commands.ts

Lines changed: 12 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { PontManager } from "pontx-manager";
22
import * as _ from "lodash";
33
import * as vscode from "vscode";
44
import {
5+
AKHelperWithLanguage,
56
findAlicloudAPIConfig,
67
findInterface,
78
getSpecInfoFromName,
@@ -200,39 +201,6 @@ export class AlicloudApiCommands {
200201
});
201202
});
202203

203-
// vscode.commands.registerCommand("alicloud.api.regenerateAPIMocks", async (event) => {
204-
// const filePaths: string[] = (event.path || "")?.split("/");
205-
// const lastMocksIndex = filePaths.lastIndexOf("mocks");
206-
// const names = filePaths.slice(lastMocksIndex + 1);
207-
// names.push(names.pop().replace(".ts", ""));
208-
209-
// if (service.pontManager.localPontSpecs?.[0]?.name) {
210-
// showProgress("重新生成 API Mocks", service.pontManager, async (log) => {
211-
// log("代码生成中...");
212-
// await wait(100);
213-
214-
// let specName, modName, apiName;
215-
// if (names.length === 3) {
216-
// [specName, modName, apiName] = names;
217-
// } else {
218-
// [specName, apiName] = names;
219-
// }
220-
// const mocksPlugin = await service.pontManager.innerManagerConfig.plugins.mocks?.instance;
221-
// const mocksOptions = await service.pontManager.innerManagerConfig.plugins.mocks?.options;
222-
// const mocksCode = await mocksPlugin.getAPIMockCode(
223-
// service.pontManager,
224-
// mocksOptions,
225-
// apiName,
226-
// modName,
227-
// specName,
228-
// );
229-
// fs.writeFileSync(event.path, mocksCode, "utf-8");
230-
231-
// log("API Mocks 生成成功!");
232-
// vscode.window.showInformationMessage("API Mocks生成成功!");
233-
// });
234-
// }
235-
// });
236204
vscode.commands.registerCommand("alicloud.api.autoImport", (...argus) => {
237205
const diagnostic = argus[0];
238206
const missingDep = argus[1];
@@ -243,6 +211,17 @@ export class AlicloudApiCommands {
243211
}
244212
});
245213

214+
vscode.commands.registerCommand("alicloud.api.akSecurityHelper", (...argus) => {
215+
const document = argus[0];
216+
if (AKHelperWithLanguage[document.languageId]) {
217+
vscode.env.openExternal(vscode.Uri.parse(AKHelperWithLanguage[document.languageId]));
218+
} else {
219+
vscode.env.openExternal(
220+
vscode.Uri.parse("https://help.aliyun.com/zh/sdk/developer-reference/ak-security-scheme"),
221+
);
222+
}
223+
});
224+
246225
vscode.commands.registerCommand("alicloud.api.fetchRemote", (config) => {
247226
const pontManager = service.pontManager;
248227

@@ -329,23 +308,5 @@ export class AlicloudApiCommands {
329308
spec: spec?.apis?.[`${result.apiKey}`],
330309
});
331310
});
332-
// vscode.commands.registerTextEditorCommand("alicloud.api.viewMocks", async (editor, edit) => {
333-
// const isSingleSpec = PontManager.checkIsSingleSpec(service.pontManager);
334-
// const result = (await findInterface(editor, !isSingleSpec, service.pontManager)) || ({} as any);
335-
// const spec = PontManager.getSpec(service.pontManager, result.specName);
336-
337-
// if (!result.apiName) {
338-
// vscode.window.showErrorMessage("未找到该 OpenAPI");
339-
// return;
340-
// }
341-
342-
// const namespace = [result.specName, result.modName, result.apiName].filter((id) => id).join("/");
343-
// const mocksFilePath = path.join(service.pontManager.innerManagerConfig.outDir, "mocks", namespace + ".ts");
344-
// vscode.commands.executeCommand("vscode.open", vscode.Uri.file(mocksFilePath));
345-
// });
346-
347-
// vscode.commands.registerCommand('alicloud.api.refreshPontExplorer', () => {
348-
// service.treeDataProvider.refresh();
349-
// });
350311
}
351312
}

src/extension.ts

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
"use strict";
2-
// The module 'vscode' contains the VS Code extensibility API
3-
// Import the module and reference it with the alias vscode in your code below
42
import { PontManager } from "pontx-manager";
53
import PontMetaFetchPlugin from "pontx-meta-fetch-plugin";
64
import * as vscode from "vscode";
@@ -16,12 +14,29 @@ import autoCompletion from "./provider/autoCompletion";
1614
import autofix from "./provider/autofix";
1715
import hoverInfo from "./provider/hoverProvider";
1816
import { getProfileInfoInstance } from "./profileManager";
17+
import { updateDiagnostics } from "./provider/linter";
1918

2019
export async function activate(context: vscode.ExtensionContext) {
21-
// if (!vscode.workspace.rootPath) {
22-
// return;
23-
// }
24-
// registerConfigSchema(context);
20+
// 插件诊断器
21+
const collection = vscode.languages.createDiagnosticCollection("test");
22+
if (vscode.window.activeTextEditor) {
23+
updateDiagnostics(vscode.window.activeTextEditor.document, collection);
24+
}
25+
context.subscriptions.push(
26+
vscode.window.onDidChangeActiveTextEditor((editor) => {
27+
if (editor) {
28+
updateDiagnostics(editor.document, collection);
29+
}
30+
}),
31+
);
32+
context.subscriptions.push(
33+
vscode.workspace.onDidChangeTextDocument((editor) => {
34+
if (editor) {
35+
updateDiagnostics(editor.document, collection);
36+
}
37+
}),
38+
);
39+
2540
const pontxConfig = await findAlicloudAPIConfig(context);
2641

2742
if (!pontxConfig) {

src/provider/autofix.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
* @description: Quick fix need to be used with a Linter
33
*/
44
import * as vscode from "vscode";
5-
import { containsAnySubstring, fileSel, getSpecInfoFromName } from "../utils";
5+
import { containsAnySubstring, fileSel, getSpecInfoFromName, SDKLanguageLabel } from "../utils";
66
import { getDepsByLanguage } from "../common/generateImport";
77
import { alicloudAPIMessageService } from "../Service";
8+
import { LintRules } from "./linter";
89

910
class CodeActionProvider {
1011
provideCodeActions(
@@ -21,16 +22,13 @@ class CodeActionProvider {
2122
const document = editor.document;
2223
const errorText = document.getText(item?.range);
2324
const service = alicloudAPIMessageService;
24-
const products = service.pontManager.localPontSpecs
25-
.map((pontSpec) => {
26-
return getSpecInfoFromName(pontSpec?.name)[0];
27-
})
25+
const products = service.pontManager.localPontSpecs.map((pontSpec) => {
26+
return getSpecInfoFromName(pontSpec?.name)[0];
27+
});
2828

29+
// 未导入依赖的诊断建议
2930
if (getDepsByLanguage(errorText, item.range)?.includes(errorText) || containsAnySubstring(errorText, products)) {
30-
const autoImportAction = new vscode.CodeAction(
31-
item.message + "导入依赖",
32-
vscode.CodeActionKind.QuickFix,
33-
);
31+
const autoImportAction = new vscode.CodeAction(item.message + "导入依赖", vscode.CodeActionKind.QuickFix);
3432
const newDiagnostic = new vscode.Diagnostic(item.range, "importLists", vscode.DiagnosticSeverity.Error);
3533
// 自动修复命令注册
3634
autoImportAction.command = {
@@ -40,6 +38,23 @@ class CodeActionProvider {
4038
};
4139
return autoImportAction;
4240
}
41+
42+
// AccessKey 可能泄露的诊断建议
43+
const lintResult = LintRules?.find((rule) => rule.source === item.source);
44+
if (lintResult) {
45+
// 自动修复命令注册
46+
const codeAction = new vscode.CodeAction(
47+
`${lintResult.methods[0]?.title}${SDKLanguageLabel[document.languageId] || "阿里云 SDK"})`,
48+
vscode.CodeActionKind.QuickFix,
49+
);
50+
codeAction.command = {
51+
title: `${lintResult.methods[0]?.title}${SDKLanguageLabel[document.languageId] || "阿里云 SDK"})`,
52+
command: lintResult.methods[0]?.command,
53+
arguments: [document],
54+
};
55+
56+
return codeAction;
57+
}
4358
});
4459

4560
return result;

src/provider/linter.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/**
2+
* @description: Linter of the Code
3+
*/
4+
import * as vscode from "vscode";
5+
import { fileSel } from "../utils";
6+
7+
class Rule {
8+
LintName: string;
9+
pattern: string;
10+
message: string;
11+
source: string;
12+
information: string;
13+
methods: Array<{
14+
title: string;
15+
command: string;
16+
}>;
17+
}
18+
export const LintRules: Array<Rule> = [
19+
{
20+
LintName: "AccessKey-KeyValue",
21+
source: "Alicloud AccessKey Lint",
22+
pattern:
23+
'(?<keyword>access|key|secret|scret|ak|sk)[^\\w\\n]*(?:\\n)?(?<separator>["\'\\s]*[:=@,]\\s*(?:"|\')?|\\w*"\\s*?,\\s*?")[\\s"\']*(?<key>[0-9A-Za-z]{14,40})(?<suffix>["\'\\s]*)',
24+
message: "工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。",
25+
information: "在此处透露了 AccessKey。",
26+
methods: [{ title: "凭据的安全使用方案", command: "alicloud.api.akSecurityHelper" }],
27+
},
28+
{
29+
LintName: "AccessKey-NewAK",
30+
source: "Alicloud AccessKey Lint",
31+
information: "在此处透露了 AccessKey。",
32+
pattern:
33+
"^LTAI(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{12}$|^LTAI(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{16}$|^LTAI(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{18}$|^LTAI(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{20}$|^LTAI(?=.*[A-Z])(?=.*[a-z])(?=.*\\d)[A-Za-z\\d]{22}$",
34+
message: "工程代码泄露可能会导致 AccessKey 泄露,并威胁账号下所有资源的安全性。",
35+
methods: [{ title: "凭据的安全使用方案", command: "alicloud.api.akSecurityHelper" }],
36+
},
37+
];
38+
39+
function searchCode(
40+
diagnosticCollection: vscode.Diagnostic[],
41+
rule: Rule,
42+
text: string,
43+
document: vscode.TextDocument,
44+
) {
45+
const regex = new RegExp(rule.pattern, "gi");
46+
let match;
47+
while ((match = regex.exec(text)) !== null) {
48+
const range = new vscode.Range(document.positionAt(match.index), document.positionAt(regex.lastIndex));
49+
const matchText = document.getText(range);
50+
if (!diagnosticCollection?.find((item) => item.code === matchText)) {
51+
diagnosticCollection.push({
52+
code: "",
53+
message: rule.message,
54+
range: range,
55+
severity: vscode.DiagnosticSeverity.Error,
56+
source: rule.source,
57+
relatedInformation: [
58+
new vscode.DiagnosticRelatedInformation(new vscode.Location(document.uri, range), rule.information),
59+
],
60+
});
61+
}
62+
}
63+
}
64+
65+
export async function updateDiagnostics(
66+
document: vscode.TextDocument,
67+
collection: vscode.DiagnosticCollection,
68+
): Promise<void> {
69+
if ((fileSel as any)?.find((sel) => sel.language === document.languageId)) {
70+
const text = document.getText();
71+
72+
let diagnosticCollection: vscode.Diagnostic[] = [];
73+
74+
LintRules.forEach((rule) => {
75+
searchCode(diagnosticCollection, rule, text, document);
76+
});
77+
78+
collection.set(document.uri, diagnosticCollection);
79+
} else {
80+
collection.clear();
81+
}
82+
}

src/utils.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,13 +487,32 @@ export const getRequiredParamsValue = (product: string, version: string, api: st
487487

488488
export const fileSel: vscode.DocumentSelector = [
489489
{ scheme: "file", language: "typescript" },
490+
{ scheme: "file", language: "javascript" },
490491
{ scheme: "file", language: "go" },
491492
{ scheme: "file", language: "java" },
492493
{ scheme: "file", language: "csharp" },
493494
{ scheme: "file", language: "python" },
494495
{ scheme: "file", language: "php" },
495496
];
496497

498+
export const SDKLanguageLabel = {
499+
typescript: "V2.0 Node.js SDK",
500+
javascript: "V2.0 Node.js SDK",
501+
go: "V2.0 Go SDK",
502+
java: "V2.0 Java SDK",
503+
python: "V2.0 Python SDK",
504+
php: "V2.0 PHP SDK",
505+
};
506+
507+
export const AKHelperWithLanguage = {
508+
typescript: "https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-node-js-access-credentials",
509+
javascript: "https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-node-js-access-credentials",
510+
java: "https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-access-credentials",
511+
go: "https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-go-access-credentials",
512+
python: "https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-python-access-credentials",
513+
php: "https://help.aliyun.com/zh/sdk/developer-reference/v2-manage-php-access-credentials",
514+
};
515+
497516
export const containsAnySubstring = (targetStr, substrings) => {
498517
const language = vscode.window.activeTextEditor?.document.languageId;
499518
if (language !== "typescript") {

0 commit comments

Comments
 (0)