const vscode = require("vscode");
const { execFile } = require("node:child_process");
const path = require("node:path");
function activate(ctx) {
const diag = vscode.languages.createDiagnosticCollection("enprot");
ctx.subscriptions.push(
vscode.workspace.onDidChangeTextDocument((e) => updateDiagnostics(diag, e.document)),
vscode.workspace.onDidOpenTextDocument((doc) => updateDiagnostics(diag, doc)),
);
for (const doc of vscode.workspace.textDocuments) {
updateDiagnostics(diag, doc);
}
ctx.subscriptions.push(
vscode.commands.registerCommand("enprot.encryptFile", (uri) => runEnprot("encrypt", uri)),
vscode.commands.registerCommand("enprot.decryptFile", (uri) => runEnprot("decrypt", uri)),
vscode.commands.registerCommand("enprot.storeFile", (uri) => runEnprot("store", uri)),
vscode.commands.registerCommand("enprot.verifyFile", (uri) => runEnprot("verify", uri)),
);
}
exports.activate = activate;
function deactivate() {}
function runEnprot(operation, uri) {
const target = uri
? uri
: vscode.window.activeTextEditor && vscode.window.activeTextEditor.document.uri;
if (!target) {
vscode.window.showWarningMessage(`enprot: no file to ${operation}.`);
return;
}
const cfg = vscode.workspace.getConfiguration("enprot");
const bin = cfg.get("binaryPath", "enprot");
const casdir = cfg.get("defaultCasdir", ".cas");
const args = [operation, "-c", casdir, target.fsPath];
vscode.window.showInputBox({
prompt: `WORD=password for ${operation} (e.g. SECRET=hunter2). Leave empty to skip.`,
password: false,
ignoreFocusOut: true,
}).then((input) => {
if (input === undefined) return;
if (input.trim()) {
args.splice(2, 0, "-w", input.trim());
}
execFile(bin, args, { cwd: path.dirname(target.fsPath) }, (err, _stdout, stderr) => {
if (err) {
vscode.window.showErrorMessage(`enprot ${operation} failed: ${stderr || err.message}`);
return;
}
vscode.window.showInformationMessage(`enprot ${operation} OK`);
});
});
}
function updateDiagnostics(collection, doc) {
const cfg = vscode.workspace.getConfiguration("enprot");
if (!cfg.get("warnOnPlaintextBeginBlock", true)) {
collection.delete(doc.uri);
return;
}
const diags = [];
let inBegin = null; let bodyClean = false;
for (let i = 0; i < doc.lineCount; i++) {
const line = doc.lineAt(i).text;
const m = line.match(/^\s*[#/]{1,2}\s*<?\(?\s*(BEGIN|END|ENCRYPTED|STORED|DATA)\b(?:\s+(\w+))?/);
if (m) {
const kw = m[1];
const word = m[2] || "";
if (kw === "BEGIN") {
inBegin = { word, startLine: i, bodyClean: false };
bodyClean = false;
} else if (kw === "ENCRYPTED" || kw === "STORED" || kw === "DATA") {
bodyClean = true;
if (inBegin) inBegin.bodyClean = true;
} else if (kw === "END") {
if (inBegin && !inBegin.bodyClean) {
const range = new vscode.Range(inBegin.startLine, 0, i, line.length);
diags.push(new vscode.Diagnostic(
range,
`enprot: plaintext inside BEGIN ${inBegin.word} block. Run enprot encrypt or enprot store before commit.`,
vscode.DiagnosticSeverity.Warning,
));
}
inBegin = null;
bodyClean = false;
}
} else if (inBegin && line.trim() !== "" && !bodyClean) {
}
}
if (diags.length) {
collection.set(doc.uri, diags);
} else {
collection.delete(doc.uri);
}
}
module.exports = { activate, deactivate };