mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-05-05 12:44:38 +00:00
Introduce a new extension for CodeMirror that masks sensitive values in .env files. This ensures that sensitive information like API keys or passwords is not displayed in plain text within the editor. The extension dynamically applies masking to values in lines matching the KEY=VALUE format, improving security during development.
81 lines
2.2 KiB
TypeScript
81 lines
2.2 KiB
TypeScript
import { EditorView, Decoration, type DecorationSet, ViewPlugin, WidgetType } from '@codemirror/view';
|
|
|
|
// Create a proper WidgetType class for the masked text
|
|
class MaskedTextWidget extends WidgetType {
|
|
constructor(private readonly _value: string) {
|
|
super();
|
|
}
|
|
|
|
eq(other: MaskedTextWidget) {
|
|
return other._value === this._value;
|
|
}
|
|
|
|
toDOM() {
|
|
const span = document.createElement('span');
|
|
span.textContent = '*'.repeat(this._value.length);
|
|
span.className = 'cm-masked-text';
|
|
|
|
return span;
|
|
}
|
|
|
|
ignoreEvent() {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
export function createEnvMaskingExtension(getFilePath: () => string | undefined) {
|
|
return ViewPlugin.fromClass(
|
|
class {
|
|
decorations: DecorationSet;
|
|
|
|
constructor(view: EditorView) {
|
|
this.decorations = this.buildDecorations(view);
|
|
}
|
|
|
|
update(update: { docChanged: boolean; view: EditorView; viewportChanged: boolean }) {
|
|
if (update.docChanged || update.viewportChanged) {
|
|
this.decorations = this.buildDecorations(update.view);
|
|
}
|
|
}
|
|
|
|
buildDecorations(view: EditorView) {
|
|
const filePath = getFilePath();
|
|
const isEnvFile = filePath?.endsWith('.env') || filePath?.includes('.env.') || filePath?.includes('/.env');
|
|
|
|
if (!isEnvFile) {
|
|
return Decoration.none;
|
|
}
|
|
|
|
const decorations: any[] = [];
|
|
const doc = view.state.doc;
|
|
|
|
for (let i = 1; i <= doc.lines; i++) {
|
|
const line = doc.line(i);
|
|
const text = line.text;
|
|
|
|
// Match lines with KEY=VALUE format
|
|
const match = text.match(/^([^=]+)=(.+)$/);
|
|
|
|
if (match && !text.trim().startsWith('#')) {
|
|
const [, key, value] = match;
|
|
const valueStart = line.from + key.length + 1;
|
|
|
|
// Create a decoration that replaces the value with asterisks
|
|
decorations.push(
|
|
Decoration.replace({
|
|
inclusive: true,
|
|
widget: new MaskedTextWidget(value),
|
|
}).range(valueStart, line.to),
|
|
);
|
|
}
|
|
}
|
|
|
|
return Decoration.set(decorations);
|
|
}
|
|
},
|
|
{
|
|
decorations: (v) => v.decorations,
|
|
},
|
|
);
|
|
}
|