diff --git a/package.json b/package.json index d338d574..556610c9 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "class-variance-authority": "^0.7.0", "clsx": "^2.1.0", "cmdk": "^1.0.0", + "crypto": "^1.0.1", "crypto-js": "^4.1.1", "d3-scale": "^4.0.2", "date-fns": "^3.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f46f3362..e69e8bba 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -293,6 +293,9 @@ importers: cmdk: specifier: ^1.0.0 version: 1.0.0(@types/react-dom@18.3.0)(@types/react@18.3.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + crypto: + specifier: ^1.0.1 + version: 1.0.1 crypto-js: specifier: ^4.1.1 version: 4.2.0 @@ -4770,6 +4773,10 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + crypto@1.0.1: + resolution: {integrity: sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig==} + deprecated: This package is no longer supported. It's now a built-in Node module. If you've depended on crypto, you should switch to the one that's built-in. + css-color-names@0.0.4: resolution: {integrity: sha512-zj5D7X1U2h2zsXOAM8EyUREBnnts6H+Jm+d1M2DbiQQcUtnqgQsMrdo8JW9R80YFUmIdBZeMu5wvYM7hcgWP/Q==} @@ -14260,6 +14267,8 @@ snapshots: crypto-js@4.2.0: {} + crypto@1.0.1: {} + css-color-names@0.0.4: {} css-jss@10.10.0: diff --git a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx index a8da9806..00d47ea8 100644 --- a/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx +++ b/src/app/(dynamic-pages)/(authenticated-pages)/(application-pages)/project/[projectSlug]/(specific-project-pages)/tfvars/page.tsx @@ -26,6 +26,8 @@ export default async function TFVarsPage({ params }: { params: unknown }) { const { projectSlug } = projectSlugParamSchema.parse(params); const project = await getSlimProjectBySlug(projectSlug); const tfvars = await getTFVarsByProjectId(project.id); + const MASTER_PASSWORD = process.env.MASTER_PASSWORD || 'digger-password'; + const ENCRYPTION_SALT = process.env.ENCRYPTION_SALT || 'digger-salt'; return (
diff --git a/src/data/admin/encryption.ts b/src/data/admin/encryption.ts new file mode 100644 index 00000000..7839fece --- /dev/null +++ b/src/data/admin/encryption.ts @@ -0,0 +1,39 @@ +import { + createCipheriv, + createDecipheriv, + pbkdf2Sync, + randomBytes, +} from 'crypto'; + +function deriveKey(password: string, salt: string): Buffer { + return pbkdf2Sync(password, salt, 100000, 32, 'sha256'); +} + +function encrypt( + text: string, + ENCRYPTION_SALT: string, + MASTER_PASSWORD: string, +): { iv: string; encryptedData: string } { + const iv = randomBytes(16); + const key = deriveKey(MASTER_PASSWORD, ENCRYPTION_SALT); + const cipher = createCipheriv('aes-256-cbc', key, iv); + let encrypted = cipher.update(text, 'utf8', 'hex'); + encrypted += cipher.final('hex'); + return { + iv: iv.toString('hex'), + encryptedData: encrypted, + }; +} + +function decrypt( + iv: string, + encryptedData: string, + ENCRYPTION_SALT: string, + MASTER_PASSWORD: string, +): string { + const key = deriveKey(MASTER_PASSWORD, ENCRYPTION_SALT); + const decipher = createDecipheriv('aes-256-cbc', key, Buffer.from(iv, 'hex')); + let decrypted = decipher.update(encryptedData, 'hex', 'utf8'); + decrypted += decipher.final('utf8'); + return decrypted; +} diff --git a/supabase/migrations/20240724080812_project_vars_table.sql b/supabase/migrations/20240724080812_project_vars_table.sql new file mode 100644 index 00000000..05070492 --- /dev/null +++ b/supabase/migrations/20240724080812_project_vars_table.sql @@ -0,0 +1,10 @@ +CREATE TABLE encrypted_env_vars ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + project_id UUID NOT NULL, + name VARCHAR(255) NOT NULL, + encrypted_value BYTEA NOT NULL, + iv BYTEA NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + updated_at TIMESTAMP WITH TIME ZONE DEFAULT now() NOT NULL, + FOREIGN KEY (project_id) REFERENCES projects (id) ON DELETE CASCADE +); \ No newline at end of file