Skip to content

Commit 8137c09

Browse files
committed
feat: Add obfuscation option
1 parent 4aeba9f commit 8137c09

File tree

3 files changed

+89
-2
lines changed

3 files changed

+89
-2
lines changed

packages/envied/lib/src/envied_base.dart

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,15 @@ class Envied {
2828
/// ```
2929
final String? name;
3030

31-
const Envied({String? path, bool? requireEnvFile, this.name})
31+
/// Allows all the values to be encrypted using a random
32+
/// generated key that is then XOR'd with the encrypted
33+
/// value when being accessed the first time.
34+
/// Please note that the values can not be offered with
35+
/// the const qualifier, but only with final.
36+
final bool obfuscate;
37+
38+
const Envied(
39+
{String? path, bool? requireEnvFile, this.name, this.obfuscate = false})
3240
: path = path ?? '.env',
3341
requireEnvFile = requireEnvFile ?? false;
3442
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import 'dart:math';
2+
3+
import 'package:analyzer/dart/element/element.dart';
4+
import 'package:source_gen/source_gen.dart';
5+
6+
/// Generate the line to be used in the generated class.
7+
/// If [value] is `null`, it means the variable definition doesn't exist
8+
/// and an [InvalidGenerationSourceError] will be thrown.
9+
///
10+
/// Since this function also does the type casting,
11+
/// an [InvalidGenerationSourceError] will also be thrown if
12+
/// the type can't be casted, or is not supported.
13+
String generateLineEncrypted(FieldElement field, String? value) {
14+
if (value == null) {
15+
throw InvalidGenerationSourceError(
16+
'Environment variable not found for field `${field.name}`.',
17+
element: field,
18+
);
19+
}
20+
21+
final rand = Random.secure();
22+
final type = field.type.getDisplayString(withNullability: false);
23+
final name = field.name;
24+
final keyName = '_enviedkey$name';
25+
26+
switch (type) {
27+
case "int":
28+
final parsed = int.tryParse(value);
29+
if (parsed == null) {
30+
throw InvalidGenerationSourceError(
31+
'Type `$type` does not align up to value `$value`.',
32+
element: field,
33+
);
34+
} else {
35+
final key = rand.nextInt(1 << 32);
36+
final encValue = parsed ^ key;
37+
return 'static final int $keyName = $key;\n'
38+
'static final int $name = $keyName ^ $encValue;';
39+
}
40+
case "bool":
41+
final lowercaseValue = value.toLowerCase();
42+
if (['true', 'false'].contains(lowercaseValue)) {
43+
final parsed = lowercaseValue == 'true';
44+
final key = rand.nextBool();
45+
final encValue = parsed ^ key;
46+
return 'static final bool $keyName = $key;\n'
47+
'static final bool $name = $keyName ^ $encValue;';
48+
} else {
49+
throw InvalidGenerationSourceError(
50+
'Type `$type` does not align up to value `$value`.',
51+
element: field,
52+
);
53+
}
54+
case "String":
55+
case "dynamic":
56+
final parsed = value.codeUnits;
57+
final key = parsed.map((e) => rand.nextInt(1 << 32)).toList(
58+
growable: false,
59+
);
60+
final encValue = List.generate(parsed.length, (i) => i, growable: false)
61+
.map((i) => parsed[i] ^ key[i])
62+
.toList(growable: false);
63+
final encName = '_envieddata$name';
64+
return 'static const List<int> $keyName = [${key.join(", ")}];\n'
65+
'static const List<int> $encName = [${encValue.join(", ")}];\n'
66+
'static final ${type == 'dynamic' ? '' : 'String'} $name = String.fromCharCodes(\n'
67+
' List.generate($encName.length, (i) => i, growable: false)\n'
68+
' .map((i) => $encName[i] ^ $keyName[i])\n'
69+
' .toList(growable: false),\n'
70+
');';
71+
default:
72+
throw InvalidGenerationSourceError(
73+
'Obfuscated envied can only handle types such as `int`, `bool` and `String`. Type `$type` is not one of them.',
74+
element: field,
75+
);
76+
}
77+
}

packages/envied_generator/lib/src/generator.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:analyzer/dart/element/element.dart';
55
import 'package:build/build.dart';
66
import 'package:envied/envied.dart';
77
import 'package:envied_generator/src/generate_line.dart';
8+
import 'package:envied_generator/src/generate_line_encrypted.dart';
89
import 'package:envied_generator/src/load_envs.dart';
910
import 'package:source_gen/source_gen.dart';
1011

@@ -32,6 +33,7 @@ class EnviedGenerator extends GeneratorForAnnotation<Envied> {
3233
requireEnvFile:
3334
annotation.read('requireEnvFile').literalValue as bool? ?? false,
3435
name: annotation.read('name').literalValue as String?,
36+
obfuscate: annotation.read('obfuscate').literalValue as bool,
3537
);
3638

3739
final envs = await loadEnvs(config.path, (error) {
@@ -56,7 +58,7 @@ class EnviedGenerator extends GeneratorForAnnotation<Envied> {
5658
} else if (Platform.environment.containsKey(varName)) {
5759
varValue = Platform.environment[varName];
5860
}
59-
return generateLine(
61+
return (config.obfuscate ? generateLineEncrypted : generateLine)(
6062
fieldEl,
6163
varValue,
6264
);

0 commit comments

Comments
 (0)