react-native-secreton 2.2.1 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,12 @@
1
1
  # react-native-secreton
2
- Config secret variables for React Native apps
2
+ A library for managing **secret environment variables** in React Native apps.
3
+ Supports integration with **Consul** and **Vault** to securely retrieve configurations.
3
4
 
4
5
  ## Installation
5
- ```sh
6
+ ```bash
6
7
  npm install react-native-secreton
8
+ # or
9
+ yarn add react-native-secreton
7
10
  ```
8
11
 
9
12
  ## Generate secret key
@@ -15,7 +18,7 @@ openssl rand -hex 32
15
18
  ## Setup
16
19
  Link the library:
17
20
  ```
18
- $ react-native link react-native-config
21
+ $ react-native link react-native-secreton
19
22
  ```
20
23
 
21
24
  - Manual Link (Android)
@@ -39,6 +42,30 @@ $ react-native link react-native-config
39
42
  + pod 'react-native-secreton', :path => '../node_modules/react-native-secreton'
40
43
  ```
41
44
 
45
+ ## Usage Examples
46
+
47
+ ### Using Consul
48
+ ```bash
49
+ export ENV_SECRET_KEY=secret-key-32-bytes
50
+ export FETCH_ENV=consul
51
+ export CONSUL_ADDR=http://consul.mycompany.com:8500
52
+ export CONSUL_PATH=mobile/myapp
53
+ export CONSUL_TOKEN=abcd1234
54
+
55
+ rn-secreton-cli .env
56
+ ```
57
+
58
+ ### Using Vault
59
+ ```bash
60
+ export ENV_SECRET_KEY=secret-key-32-bytes
61
+ export FETCH_ENV=vault
62
+ export VAULT_ADDR=http://vault.mycompany.com:8200
63
+ export VAULT_PATH=secret/data/mobile/myapp
64
+ export VAULT_TOKEN=abcd1234
65
+
66
+ rn-secreton-cli .env
67
+ ```
68
+
42
69
  ## Native Usage
43
70
 
44
71
  ### Android
@@ -63,8 +90,6 @@ Then in Xcode build settings:
63
90
  2. Use on native iOS (Objective‑C/Swift)
64
91
  ```objective‑c
65
92
  let apiKey = ProcessInfo.processInfo.environment["GEO_APK_API_KEY"]
66
-
67
- or
68
-
93
+ # or
69
94
  let apiKey = Bundle.main.object(forInfoDictionaryKey: "GEO_APK_API_KEY") as? String
70
95
  ```
@@ -18,9 +18,10 @@ buildscript {
18
18
 
19
19
  apply plugin: "com.android.library"
20
20
  apply plugin: "kotlin-android"
21
-
22
21
  apply plugin: "com.facebook.react"
23
22
 
23
+ apply from: "secreton.gradle"
24
+
24
25
  def getExtOrIntegerDefault(name) {
25
26
  return rootProject.ext.has(name) ? rootProject.ext.get(name) : (project.properties["Secreton_" + name]).toInteger()
26
27
  }
@@ -33,6 +34,7 @@ android {
33
34
  defaultConfig {
34
35
  minSdkVersion getExtOrIntegerDefault("minSdkVersion")
35
36
  targetSdkVersion getExtOrIntegerDefault("targetSdkVersion")
37
+ buildConfigField "String", "ENV_SECRET_KEY", "\"${getSecretKeyFromEnv(project)}\""
36
38
  }
37
39
 
38
40
  buildFeatures {
@@ -38,18 +38,30 @@ ext.loadEnvFile = { File file ->
38
38
  return map
39
39
  }
40
40
 
41
+ ext.getEnvFile = { appProject ->
42
+ def androidDir = appProject.rootProject.projectDir
43
+ def workspaceRoot = findWorkspaceRoot(androidDir)
44
+
45
+ def envFileName = appProject.findProperty("ENVFILE") ?: System.getenv("ENVFILE") ?: ".env"
46
+ def envFile = new File(workspaceRoot, envFileName)
47
+
48
+ return envFile
49
+ }
50
+
51
+ ext.getSecretKeyFromEnv = { appProject ->
52
+ def envFile = getEnvFile(appProject)
53
+ def envMap = loadEnvFile(envFile)
54
+
55
+ return envMap["ENV_SECRET_KEY"] ?: ""
56
+ }
57
+
41
58
  ext.ensureLocalEnv = { appProject ->
42
59
  if (appProject.ext.has("localEnv")) return
43
60
 
44
61
  appProject.ext.localEnv = [:]
45
62
  appProject.ext.env = [:]
46
-
47
- def androidDir = appProject.rootProject.projectDir
48
- def workspaceRoot = findWorkspaceRoot(androidDir)
49
- def rootDir = workspaceRoot
50
-
51
- def envFileName = appProject.findProperty("ENVFILE") ?: System.getenv("ENVFILE") ?: ".env"
52
- def envFile = new File(rootDir, envFileName)
63
+
64
+ def envFile = getEnvFile(appProject)
53
65
 
54
66
  if (envFile.exists()) {
55
67
  appProject.ext.localEnv.putAll(loadEnvFile(envFile))
@@ -72,23 +84,21 @@ ext.decryptValue = { String encrypted, String secretKey ->
72
84
  proc.waitFor()
73
85
 
74
86
  if (proc.exitValue() != 0) {
75
- throw new RuntimeException(proc.err.text)
87
+ throw new RuntimeException("❌ OpenSSL decrypt failed: ${proc.err.text}")
88
+ }
89
+
90
+ def output = proc.in.text.trim()
91
+ if (!output) {
92
+ throw new RuntimeException("❌ Decryption returned empty value")
76
93
  }
77
94
 
78
- return proc.in.text.trim()
95
+ return output
79
96
  }
80
97
 
81
98
  ext.loadAndDecryptEnv = { appProject ->
82
99
  ensureLocalEnv(appProject)
83
-
84
- def androidDir = appProject.rootProject.projectDir
85
- def rootDir = findWorkspaceRoot(androidDir)
86
-
87
- def envFileName =
88
- appProject.findProperty("ENVFILE")
89
- ?: System.getenv("ENVFILE")
90
- ?: ".env"
91
- def envFile = new File(rootDir, envFileName)
100
+
101
+ def envFile = getEnvFile(appProject)
92
102
  if (!envFile.exists()) return
93
103
 
94
104
  def secretKey = getSecretKey(appProject)
@@ -97,8 +107,11 @@ ext.loadAndDecryptEnv = { appProject ->
97
107
  def envMap = loadEnvFile(envFile)
98
108
  envMap.each { k, v ->
99
109
  def value = v
100
- if (v instanceof String && v.startsWith("enc:")) {
101
- value = decryptValue(v.substring(4), secretKey)
110
+ if (v instanceof String && v.startsWith("Secreton:")) {
111
+ value = decryptValue(v.substring(9), secretKey)
112
+ }
113
+ if (value == null) {
114
+ throw new RuntimeException("❌ Failed to resolve env ${k}")
102
115
  }
103
116
  appProject.ext.env[k] = value
104
117
  }
@@ -2,22 +2,96 @@ package com.secreton
2
2
 
3
3
  import com.facebook.react.bridge.ReactApplicationContext
4
4
  import com.facebook.react.module.annotations.ReactModule
5
+ import android.util.Base64
6
+ import java.security.SecureRandom
7
+ import javax.crypto.Cipher
8
+ import javax.crypto.SecretKeyFactory
9
+ import javax.crypto.spec.IvParameterSpec
10
+ import javax.crypto.spec.PBEKeySpec
11
+ import javax.crypto.spec.SecretKeySpec
5
12
 
6
13
  @ReactModule(name = SecretonModule.NAME)
7
- class SecretonModule(reactContext: ReactApplicationContext) :
8
- NativeSecretonSpec(reactContext) {
14
+ class SecretonModule(
15
+ reactContext: ReactApplicationContext
16
+ ) : NativeSecretonSpec(reactContext) {
9
17
 
10
- override fun getName(): String {
11
- return NAME
18
+ companion object {
19
+ const val NAME = "Secreton"
20
+ private const val PREFIX = "$NAME:"
21
+ private const val ITERATIONS = 100_000
12
22
  }
13
23
 
14
- // Example method
15
- // See https://reactnative.dev/docs/native-modules-android
16
- override fun multiply(a: Double, b: Double): Double {
17
- return a * b
24
+ override fun getName() = NAME
25
+
26
+ override fun encrypt(value: String): String {
27
+ val encrypted = encryptOpenSSL(value, getSecretKey())
28
+ return PREFIX + encrypted
18
29
  }
19
30
 
20
- companion object {
21
- const val NAME = "Secreton"
31
+ override fun decrypt(value: String): String {
32
+ if (!value.startsWith(PREFIX)) return value
33
+ return decryptOpenSSL(value.removePrefix(PREFIX), getSecretKey())
34
+ }
35
+
36
+ private fun getSecretKey(): String {
37
+ return BuildConfig.ENV_SECRET_KEY
38
+ }
39
+
40
+ private fun encryptOpenSSL(value: String, key: String): String {
41
+ val salt = ByteArray(8)
42
+ SecureRandom().nextBytes(salt)
43
+
44
+ val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
45
+ val spec = PBEKeySpec(
46
+ key.toCharArray(),
47
+ salt,
48
+ ITERATIONS,
49
+ (32 + 16) * 8 // key + iv in bits
50
+ )
51
+
52
+ val keyIv = factory.generateSecret(spec).encoded
53
+ val aesKey = SecretKeySpec(keyIv.copyOfRange(0, 32), "AES")
54
+ val iv = IvParameterSpec(keyIv.copyOfRange(32, 48))
55
+
56
+ val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
57
+ cipher.init(Cipher.ENCRYPT_MODE, aesKey, iv)
58
+
59
+ val encrypted = cipher.doFinal(value.toByteArray(Charsets.UTF_8))
60
+
61
+ val output = ByteArray(16 + encrypted.size)
62
+ System.arraycopy("Salted__".toByteArray(), 0, output, 0, 8)
63
+ System.arraycopy(salt, 0, output, 8, 8)
64
+ System.arraycopy(encrypted, 0, output, 16, encrypted.size)
65
+
66
+ return Base64.encodeToString(output, Base64.NO_WRAP)
67
+ }
68
+
69
+ private fun decryptOpenSSL(encrypted: String, key: String): String {
70
+ val decoded = Base64.decode(encrypted, Base64.NO_WRAP)
71
+
72
+ val magic = String(decoded.copyOfRange(0, 8))
73
+ if (magic != "Salted__") {
74
+ throw IllegalArgumentException("Invalid OpenSSL salt header")
75
+ }
76
+
77
+ val salt = decoded.copyOfRange(8, 16)
78
+ val cipherText = decoded.copyOfRange(16, decoded.size)
79
+
80
+ val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
81
+ val spec = PBEKeySpec(
82
+ key.toCharArray(),
83
+ salt,
84
+ ITERATIONS,
85
+ (32 + 16) * 8
86
+ )
87
+
88
+ val keyIv = factory.generateSecret(spec).encoded
89
+ val aesKey = SecretKeySpec(keyIv.copyOfRange(0, 32), "AES")
90
+ val iv = IvParameterSpec(keyIv.copyOfRange(32, 48))
91
+
92
+ val cipher = Cipher.getInstance("AES/CBC/PKCS7Padding")
93
+ cipher.init(Cipher.DECRYPT_MODE, aesKey, iv)
94
+
95
+ return String(cipher.doFinal(cipherText), Charsets.UTF_8)
22
96
  }
23
97
  }
@@ -25,7 +25,7 @@ class SecretonPackage : BaseReactPackage() {
25
25
  false, // canOverrideExistingModule
26
26
  false, // needsEagerInit
27
27
  false, // isCxxModule
28
- true // isTurboModule
28
+ true // isTurboModule
29
29
  )
30
30
  moduleInfos
31
31
  }
@@ -5,6 +5,10 @@ export function encrypt(value, key) {
5
5
  const cmd = `printf "%s" "${value}" | openssl enc -aes-256-cbc -a -A -salt -pbkdf2 -iter 100000 -pass pass:${key}`;
6
6
  return execSync(cmd).toString().trim();
7
7
  }
8
+ export function decrypt(encrypted, key) {
9
+ const cmd = `printf "%s" "${encrypted}" | openssl enc -aes-256-cbc -a -A -d -salt -pbkdf2 -iter 100000 -pass pass:${key}`;
10
+ return execSync(cmd).toString().trim();
11
+ }
8
12
  export function readExistingEnv(envFile) {
9
13
  if (!fs.existsSync(envFile))
10
14
  return new Set();
@@ -60,7 +64,7 @@ export async function generateEnv(options) {
60
64
  }
61
65
  const newLines = entries
62
66
  .filter(({ key }) => !existingKeys.has(key))
63
- .map(({ key, value }) => `${key}=enc:${encrypt(value, secretKey)}`);
67
+ .map(({ key, value }) => `${key}=Secreton:${encrypt(value, secretKey)}`);
64
68
  if (newLines.length === 0) {
65
69
  console.log('ℹ️ [Node] Secreton no new env keys to add');
66
70
  return;
package/ios/Secreton.mm CHANGED
@@ -1,21 +1,100 @@
1
1
  #import "Secreton.h"
2
2
 
3
+ #import <CommonCrypto/CommonCrypto.h>
4
+
3
5
  @implementation Secreton
4
- - (NSNumber *)multiply:(double)a b:(double)b {
5
- NSNumber *result = @(a * b);
6
6
 
7
- return result;
7
+ static NSString * const kPrefix = @"Secreton:";
8
+
9
+ + (NSString *)moduleName
10
+ {
11
+ return @"Secreton";
8
12
  }
9
13
 
10
- - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
11
- (const facebook::react::ObjCTurboModule::InitParams &)params
14
+ #pragma mark - TurboModule methods
15
+
16
+ - (NSString *)encrypt:(NSString *)value
12
17
  {
13
- return std::make_shared<facebook::react::NativeSecretonSpecJSI>(params);
18
+ NSString *key = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"ENV_SECRET_KEY"];
19
+ if (!key || key.length == 0) {
20
+ return nil;
21
+ }
22
+
23
+ NSString *encrypted = [self aes:kCCEncrypt value:value key:key];
24
+ return [kPrefix stringByAppendingString:encrypted];
14
25
  }
15
26
 
16
- + (NSString *)moduleName
27
+ - (NSString *)decrypt:(NSString *)value
17
28
  {
18
- return @"Secreton";
29
+ if (![value hasPrefix:kPrefix]) {
30
+ return value;
31
+ }
32
+
33
+ NSString *key = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"ENV_SECRET_KEY"];
34
+ if (!key || key.length == 0) {
35
+ return nil;
36
+ }
37
+
38
+ NSString *raw = [value substringFromIndex:kPrefix.length];
39
+ return [self aes:kCCDecrypt value:raw key:key];
40
+ }
41
+
42
+ #pragma mark - AES core
43
+
44
+ - (NSString *)aes:(CCOperation)operation
45
+ value:(NSString *)value
46
+ key:(NSString *)key
47
+ {
48
+ NSData *data = nil;
49
+
50
+ if (operation == kCCEncrypt) {
51
+ data = [value dataUsingEncoding:NSUTF8StringEncoding];
52
+ } else {
53
+ data = [[NSData alloc] initWithBase64EncodedString:value options:0];
54
+ if (!data) return nil;
55
+ }
56
+
57
+ // Key 32 byte (AES-256)
58
+ NSMutableData *keyData = [NSMutableData dataWithLength:kCCKeySizeAES256];
59
+ NSData *rawKey = [key dataUsingEncoding:NSUTF8StringEncoding];
60
+ memcpy(keyData.mutableBytes, rawKey.bytes, MIN(rawKey.length, kCCKeySizeAES256));
61
+
62
+ size_t outLength = 0;
63
+ NSMutableData *outData = [NSMutableData dataWithLength:data.length + kCCBlockSizeAES128];
64
+
65
+ CCCryptorStatus status = CCCrypt(
66
+ operation,
67
+ kCCAlgorithmAES,
68
+ kCCOptionPKCS7Padding,
69
+ keyData.bytes,
70
+ kCCKeySizeAES256,
71
+ NULL, // IV zero
72
+ data.bytes,
73
+ data.length,
74
+ outData.mutableBytes,
75
+ outData.length,
76
+ &outLength
77
+ );
78
+
79
+ if (status != kCCSuccess) {
80
+ return nil;
81
+ }
82
+
83
+ outData.length = outLength;
84
+
85
+ if (operation == kCCEncrypt) {
86
+ return [outData base64EncodedStringWithOptions:0];
87
+ }
88
+
89
+ return [[NSString alloc] initWithData:outData encoding:NSUTF8StringEncoding];
90
+ }
91
+
92
+ #pragma mark - TurboModule binding
93
+
94
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
95
+ (const facebook::react::ObjCTurboModule::InitParams &)params
96
+ {
97
+ return std::make_shared<facebook::react::NativeSecretonSpecJSI>(params);
19
98
  }
20
99
 
21
100
  @end
@@ -1 +1 @@
1
- {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeSecreton.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAMpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,UAAU,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeSecreton.ts"],"mappings":";;AAAA,SAASA,mBAAmB,QAA0B,cAAc;AAOpE,eAAeA,mBAAmB,CAACC,YAAY,CAAO,UAAU,CAAC","ignoreList":[]}
@@ -7,6 +7,10 @@ export function encrypt(value, key) {
7
7
  const cmd = `printf "%s" "${value}" | openssl enc -aes-256-cbc -a -A -salt -pbkdf2 -iter 100000 -pass pass:${key}`;
8
8
  return execSync(cmd).toString().trim();
9
9
  }
10
+ export function decrypt(encrypted, key) {
11
+ const cmd = `printf "%s" "${encrypted}" | openssl enc -aes-256-cbc -a -A -d -salt -pbkdf2 -iter 100000 -pass pass:${key}`;
12
+ return execSync(cmd).toString().trim();
13
+ }
10
14
  export function readExistingEnv(envFile) {
11
15
  if (!fs.existsSync(envFile)) return new Set();
12
16
  const content = fs.readFileSync(envFile, 'utf8');
@@ -67,7 +71,7 @@ export async function generateEnv(options) {
67
71
  }) => !existingKeys.has(key)).map(({
68
72
  key,
69
73
  value
70
- }) => `${key}=enc:${encrypt(value, secretKey)}`);
74
+ }) => `${key}=Secreton:${encrypt(value, secretKey)}`);
71
75
  if (newLines.length === 0) {
72
76
  console.log('ℹ️ [Node] Secreton no new env keys to add');
73
77
  return;
@@ -1 +1 @@
1
- {"version":3,"names":["path","fs","execSync","encrypt","value","key","cmd","toString","trim","readExistingEnv","envFile","existsSync","Set","content","readFileSync","split","map","line","filter","Boolean","startsWith","length","fetchConsul","addr","token","headers","res","fetch","ok","Error","data","json","item","Value","Key","replace","Buffer","from","fetchVault","Object","entries","String","generateEnv","options","secretKey","fetchEnv","fileName","basename","existingKeys","vault","consul","newLines","has","console","log","appendFileSync","join"],"sourceRoot":"../../src","sources":["NodeSecreton.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["path","fs","execSync","encrypt","value","key","cmd","toString","trim","decrypt","encrypted","readExistingEnv","envFile","existsSync","Set","content","readFileSync","split","map","line","filter","Boolean","startsWith","length","fetchConsul","addr","token","headers","res","fetch","ok","Error","data","json","item","Value","Key","replace","Buffer","from","fetchVault","Object","entries","String","generateEnv","options","secretKey","fetchEnv","fileName","basename","existingKeys","vault","consul","newLines","has","console","log","appendFileSync","join"],"sourceRoot":"../../src","sources":["NodeSecreton.ts"],"mappings":"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","ignoreList":[]}
@@ -1,7 +1,10 @@
1
1
  "use strict";
2
2
 
3
3
  import Secreton from "./NativeSecreton.js";
4
- export function multiply(a, b) {
5
- return Secreton.multiply(a, b);
4
+ export function encrypt(value) {
5
+ return Secreton.encrypt(value);
6
+ }
7
+ export function decrypt(encrypted) {
8
+ return Secreton.decrypt(encrypted);
6
9
  }
7
10
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["Secreton","multiply","a","b"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,QAAQ,MAAM,qBAAkB;AAEvC,OAAO,SAASC,QAAQA,CAACC,CAAS,EAAEC,CAAS,EAAU;EACrD,OAAOH,QAAQ,CAACC,QAAQ,CAACC,CAAC,EAAEC,CAAC,CAAC;AAChC","ignoreList":[]}
1
+ {"version":3,"names":["Secreton","encrypt","value","decrypt","encrypted"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,OAAOA,QAAQ,MAAM,qBAAkB;AAEvC,OAAO,SAASC,OAAOA,CAACC,KAAa,EAAU;EAC7C,OAAOF,QAAQ,CAACC,OAAO,CAACC,KAAK,CAAC;AAChC;AAEA,OAAO,SAASC,OAAOA,CAACC,SAAiB,EAAU;EACjD,OAAOJ,QAAQ,CAACG,OAAO,CAACC,SAAS,CAAC;AACpC","ignoreList":[]}
@@ -1,6 +1,7 @@
1
1
  import { type TurboModule } from 'react-native';
2
2
  export interface Spec extends TurboModule {
3
- multiply(a: number, b: number): number;
3
+ encrypt(value: string): string;
4
+ decrypt(encrypted: string): string;
4
5
  }
5
6
  declare const _default: Spec;
6
7
  export default _default;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeSecreton.d.ts","sourceRoot":"","sources":["../../../src/NativeSecreton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CACxC;;AAED,wBAAkE"}
1
+ {"version":3,"file":"NativeSecreton.d.ts","sourceRoot":"","sources":["../../../src/NativeSecreton.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAAC;CACpC;;AAED,wBAAkE"}
@@ -24,6 +24,7 @@ export interface GenerateEnvOptions {
24
24
  fetchEnv?: 'consul' | 'vault';
25
25
  }
26
26
  export declare function encrypt(value: string, key: string): string;
27
+ export declare function decrypt(encrypted: string, key: string): string;
27
28
  export declare function readExistingEnv(envFile: string): Set<string>;
28
29
  export declare function generateEnv(options: GenerateEnvOptions): Promise<void>;
29
30
  //# sourceMappingURL=NodeSecreton.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"NodeSecreton.d.ts","sourceRoot":"","sources":["../../../src/NodeSecreton.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAGjD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAc5D;AAwCD,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,iBAgC5D"}
1
+ {"version":3,"file":"NodeSecreton.d.ts","sourceRoot":"","sources":["../../../src/NodeSecreton.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,QAAQ;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,QAAQ,GAAG;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,CAAC;CAC/B;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAGjD;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,UAGrD;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAc5D;AAwCD,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,iBAgC5D"}
@@ -1,2 +1,3 @@
1
- export declare function multiply(a: number, b: number): number;
1
+ export declare function encrypt(value: string): string;
2
+ export declare function decrypt(encrypted: string): string;
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,wBAAgB,QAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAErD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.tsx"],"names":[],"mappings":"AAEA,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,wBAAgB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEjD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-secreton",
3
- "version": "2.2.1",
3
+ "version": "2.3.1",
4
4
  "description": "Config secret variables for React Native apps",
5
5
  "keywords": [
6
6
  "react-native",
@@ -48,7 +48,8 @@
48
48
  "nitrogen": "nitrogen",
49
49
  "typecheck": "tsc",
50
50
  "lint": "eslint \"src/**/*.{js,ts,tsx}\"",
51
- "test": "jest",
51
+ "test": "jest -c jest.config.cjs",
52
+ "test:coverage": "jest -c jest.config.cjs --coverage --coverageDirectory=.coverage",
52
53
  "release": "release-it --only-version"
53
54
  },
54
55
  "bin": {
@@ -76,6 +77,7 @@
76
77
  "@react-native/eslint-config": "0.83.0",
77
78
  "@release-it/conventional-changelog": "^10.0.1",
78
79
  "@types/jest": "^29.5.14",
80
+ "@types/node": "^25.0.5",
79
81
  "@types/react": "^19.2.0",
80
82
  "commitlint": "^19.8.1",
81
83
  "del-cli": "^6.0.0",
@@ -90,6 +92,7 @@
90
92
  "react-native": "0.83.0",
91
93
  "react-native-builder-bob": "^0.40.17",
92
94
  "release-it": "^19.0.4",
95
+ "ts-jest": "^29.4.6",
93
96
  "turbo": "^2.5.6",
94
97
  "typescript": "^5.9.2"
95
98
  },
@@ -23,5 +23,7 @@ Pod::Spec.new do |s|
23
23
  :execution_position => :before_compile
24
24
  }
25
25
 
26
+ s.frameworks = 'Foundation', 'Security'
27
+
26
28
  install_modules_dependencies(s)
27
29
  end
@@ -1,7 +1,8 @@
1
1
  import { TurboModuleRegistry, type TurboModule } from 'react-native';
2
2
 
3
3
  export interface Spec extends TurboModule {
4
- multiply(a: number, b: number): number;
4
+ encrypt(value: string): string;
5
+ decrypt(encrypted: string): string;
5
6
  }
6
7
 
7
8
  export default TurboModuleRegistry.getEnforcing<Spec>('Secreton');
@@ -37,6 +37,11 @@ export function encrypt(value: string, key: string) {
37
37
  return execSync(cmd).toString().trim();
38
38
  }
39
39
 
40
+ export function decrypt(encrypted: string, key: string) {
41
+ const cmd = `printf "%s" "${encrypted}" | openssl enc -aes-256-cbc -a -A -d -salt -pbkdf2 -iter 100000 -pass pass:${key}`;
42
+ return execSync(cmd).toString().trim();
43
+ }
44
+
40
45
  export function readExistingEnv(envFile: string): Set<string> {
41
46
  if (!fs.existsSync(envFile)) return new Set();
42
47
 
@@ -113,7 +118,7 @@ export async function generateEnv(options: GenerateEnvOptions) {
113
118
 
114
119
  const newLines = entries
115
120
  .filter(({ key }) => !existingKeys.has(key))
116
- .map(({ key, value }) => `${key}=enc:${encrypt(value, secretKey)}`);
121
+ .map(({ key, value }) => `${key}=Secreton:${encrypt(value, secretKey)}`);
117
122
 
118
123
  if (newLines.length === 0) {
119
124
  console.log('ℹ️ [Node] Secreton no new env keys to add');
package/src/index.tsx CHANGED
@@ -1,5 +1,9 @@
1
1
  import Secreton from './NativeSecreton';
2
2
 
3
- export function multiply(a: number, b: number): number {
4
- return Secreton.multiply(a, b);
3
+ export function encrypt(value: string): string {
4
+ return Secreton.encrypt(value);
5
+ }
6
+
7
+ export function decrypt(encrypted: string): string {
8
+ return Secreton.decrypt(encrypted);
5
9
  }