devendra-skill-package 1.0.2
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/.github/skills/boiler-project-generator/SKILL.md +103 -0
- package/.github/skills/boiler-project-generator/assets/templates/android/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/angular/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/flutter/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/ios/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/README.md +3 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/api-setup.ts +76 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/component-primary-button.tsx +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/env-config.env +4 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-auth-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-dashboard-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/feature-profile-screen.tsx +45 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/hook-use-app-boot.ts +11 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/navigation-setup.tsx +25 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/project-structure.md +24 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-hooks.ts +5 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-session-slice.ts +29 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/redux-store.ts +15 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/theme-index.ts +23 -0
- package/.github/skills/boiler-project-generator/assets/templates/react-native/utils-helpers.ts +7 -0
- package/.github/skills/boiler-project-generator/metadata.yaml +29 -0
- package/.github/skills/boiler-project-generator/references/android.md +19 -0
- package/.github/skills/boiler-project-generator/references/angular.md +14 -0
- package/.github/skills/boiler-project-generator/references/flutter.md +17 -0
- package/.github/skills/boiler-project-generator/references/ios.md +17 -0
- package/.github/skills/boiler-project-generator/references/react-native.md +103 -0
- package/.github/skills/boiler-project-generator/references/react.md +17 -0
- package/.github/skills/boiler-project-generator/scripts/angular.js +43 -0
- package/.github/skills/boiler-project-generator/scripts/generate-project.mjs +107 -0
- package/.github/skills/boiler-project-generator/scripts/init.js +105 -0
- package/.github/skills/boiler-project-generator/scripts/react-native.js +1041 -0
- package/.github/skills/boiler-project-generator/scripts/react.js +32 -0
- package/.github/skills/clean-architecture-generator/SKILL.md +235 -0
- package/.github/skills/clean-architecture-generator/references/architecture-core.md +126 -0
- package/.github/skills/clean-architecture-generator/references/platform-angular.md +374 -0
- package/.github/skills/clean-architecture-generator/references/platform-flutter.md +366 -0
- package/.github/skills/clean-architecture-generator/references/platform-react-native.md +590 -0
- package/.github/skills/clean-architecture-generator/references/platform-react.md +261 -0
- package/.github/skills/code-test-coverage-analyzer/SKILL.md +1358 -0
- package/.github/skills/code-test-coverage-analyzer/assets/config-questionnaire.md +118 -0
- package/.github/skills/code-test-coverage-analyzer/assets/conflict-resolution-protocol.md +99 -0
- package/.github/skills/code-test-coverage-analyzer/assets/coverage-calculation-rules.md +294 -0
- package/.github/skills/code-test-coverage-analyzer/assets/multi-strategy-search.md +145 -0
- package/.github/skills/code-test-coverage-analyzer/assets/output-file-contracts.md +651 -0
- package/.github/skills/code-test-coverage-analyzer/assets/status-definitions.md +273 -0
- package/.github/skills/code-test-coverage-analyzer/examples/example-invocation.md +208 -0
- package/.github/skills/code-test-coverage-analyzer/references/android-patterns.md +229 -0
- package/.github/skills/code-test-coverage-analyzer/references/angular-patterns.md +177 -0
- package/.github/skills/code-test-coverage-analyzer/references/backend-patterns.md +383 -0
- package/.github/skills/code-test-coverage-analyzer/references/flutter-patterns.md +254 -0
- package/.github/skills/code-test-coverage-analyzer/references/ios-patterns.md +228 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-native-patterns.md +431 -0
- package/.github/skills/code-test-coverage-analyzer/references/react-patterns.md +206 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/detect-platform.ps1 +261 -0
- package/.github/skills/code-test-coverage-analyzer/scripts/scan-repository.ps1 +242 -0
- package/.github/skills/code-test-coverage-analyzer/templates/brd-compliance-row.md +107 -0
- package/.github/skills/code-test-coverage-analyzer/templates/test-case-row.md +130 -0
- package/.github/skills/code-test-coverage-analyzer/templates/user-story-compliance-row.md +170 -0
- package/.github/skills/custom-font-integrator/SKILL.md +373 -0
- package/.github/skills/custom-font-integrator/assets/FontTestScreen.js +150 -0
- package/.github/skills/custom-font-integrator/references/example-info-plist.xml +21 -0
- package/.github/skills/custom-font-integrator/scripts/setup-fonts.sh +99 -0
- package/.github/skills/encryption-decryption-utility/Instructions.md +282 -0
- package/.github/skills/encryption-decryption-utility/README.md +222 -0
- package/.github/skills/encryption-decryption-utility/SKILL.md +645 -0
- package/.github/skills/encryption-decryption-utility/assets/encryption-config-template.json +13 -0
- package/.github/skills/encryption-decryption-utility/assets/platform-matrix.json +180 -0
- package/.github/skills/encryption-decryption-utility/metadata.yaml +51 -0
- package/.github/skills/encryption-decryption-utility/references/android-crypto.md +157 -0
- package/.github/skills/encryption-decryption-utility/references/angular-crypto.md +130 -0
- package/.github/skills/encryption-decryption-utility/references/approach-1-webcrypto.md +95 -0
- package/.github/skills/encryption-decryption-utility/references/approach-2-rsa-aes-cbc.md +111 -0
- package/.github/skills/encryption-decryption-utility/references/approach-3-aes-sharedkey.md +114 -0
- package/.github/skills/encryption-decryption-utility/references/architecture.md +233 -0
- package/.github/skills/encryption-decryption-utility/references/backend-alignment.md +506 -0
- package/.github/skills/encryption-decryption-utility/references/dependency-guidance.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/ec-implementation.md +832 -0
- package/.github/skills/encryption-decryption-utility/references/error-handling-playbook.md +234 -0
- package/.github/skills/encryption-decryption-utility/references/flutter-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/ios-crypto.md +338 -0
- package/.github/skills/encryption-decryption-utility/references/native-rsa-implementation.md +373 -0
- package/.github/skills/encryption-decryption-utility/references/node-crypto.md +86 -0
- package/.github/skills/encryption-decryption-utility/references/react-crypto.md +105 -0
- package/.github/skills/encryption-decryption-utility/references/react-native-crypto.md +1001 -0
- package/.github/skills/encryption-decryption-utility/references/react-web-crypto.md +668 -0
- package/.github/skills/encryption-decryption-utility/references/security-checklist.md +176 -0
- package/.github/skills/encryption-decryption-utility/references/web-crypto.md +74 -0
- package/.github/skills/encryption-decryption-utility/scripts/detect.js +128 -0
- package/.github/skills/encryption-decryption-utility/scripts/install.js +1713 -0
- package/.github/skills/encryption-decryption-utility/scripts/setup.js +800 -0
- package/.github/skills/encryption-decryption-utility/template.md +241 -0
- package/.github/skills/encryption-decryption-utility/templates/config-questionnaire.md +467 -0
- package/.github/skills/encryption-decryption-utility/templates/delivery-checklist.md +206 -0
- package/.github/skills/encryption-decryption-utility/templates/implementation-plan.md +304 -0
- package/.github/skills/feature-generator/SKILL.MD +2741 -0
- package/.github/skills/feature-generator/assets/fingerprint-schema.json +385 -0
- package/.github/skills/feature-generator/assets/spec-template.md +172 -0
- package/.github/skills/feature-generator/assets/transform-rules.json +82 -0
- package/.github/skills/feature-generator/references/api-envelope-patterns.md +327 -0
- package/.github/skills/feature-generator/references/screen-logic-patterns.md +399 -0
- package/.github/skills/feature-generator/references/state-library-patterns.md +464 -0
- package/.github/skills/figma-ui-mapper/README.md +34 -0
- package/.github/skills/figma-ui-mapper/SKILL.md +101 -0
- package/.github/skills/figma-ui-mapper/assets/templates/component-map-template.md +30 -0
- package/.github/skills/figma-ui-mapper/assets/templates/generation-report-template.md +49 -0
- package/.github/skills/figma-ui-mapper/metadata.yaml +22 -0
- package/.github/skills/figma-ui-mapper/references/android.md +11 -0
- package/.github/skills/figma-ui-mapper/references/angular.md +18 -0
- package/.github/skills/figma-ui-mapper/references/capacitor.md +18 -0
- package/.github/skills/figma-ui-mapper/references/common.md +23 -0
- package/.github/skills/figma-ui-mapper/references/expo.md +18 -0
- package/.github/skills/figma-ui-mapper/references/flutter.md +18 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/angular/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/capacitor/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/expo/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/flutter/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/ionic/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/kotlin-multiplatform/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-android/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/native-ios/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/next/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/nuxt/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react/rules.md +19 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/react-native/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/svelte/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/frameworks/vue/rules.md +23 -0
- package/.github/skills/figma-ui-mapper/references/ionic.md +18 -0
- package/.github/skills/figma-ui-mapper/references/ios.md +11 -0
- package/.github/skills/figma-ui-mapper/references/kotlin-multiplatform.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-android.md +18 -0
- package/.github/skills/figma-ui-mapper/references/native-ios.md +18 -0
- package/.github/skills/figma-ui-mapper/references/next.md +18 -0
- package/.github/skills/figma-ui-mapper/references/nuxt.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react-figma-guideline.md +21 -0
- package/.github/skills/figma-ui-mapper/references/react-native.md +18 -0
- package/.github/skills/figma-ui-mapper/references/react.md +18 -0
- package/.github/skills/figma-ui-mapper/references/script-usage.md +46 -0
- package/.github/skills/figma-ui-mapper/references/svelte.md +18 -0
- package/.github/skills/figma-ui-mapper/references/vue.md +18 -0
- package/.github/skills/figma-ui-mapper/references/workflow.md +74 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-page-through-figma.js +635 -0
- package/.github/skills/figma-ui-mapper/scripts/generate-react-page-through-figma.js +4 -0
- package/.github/skills/frontend-task-breakdown/SKILL.md +734 -0
- package/.github/skills/md-file-converter/SKILL.md +291 -0
- package/.github/skills/md-file-converter/config/formats.json +114 -0
- package/.github/skills/md-file-converter/extractors/extract-docx.sh +120 -0
- package/.github/skills/md-file-converter/extractors/extract-eml.py +108 -0
- package/.github/skills/md-file-converter/extractors/extract-pdf.sh +97 -0
- package/.github/skills/md-file-converter/extractors/extract-pptx.py +143 -0
- package/.github/skills/md-file-converter/extractors/extract-xlsx.py +175 -0
- package/.github/skills/md-file-converter/scripts/check-tools.sh +47 -0
- package/.github/skills/md-file-converter/scripts/extract-archive.sh +83 -0
- package/.github/skills/md-file-converter/scripts/extract-ocr.sh +77 -0
- package/.github/skills/md-file-converter/scripts/get-metadata.sh +57 -0
- package/.github/skills/md-file-converter/templates/output.md +73 -0
- package/.github/skills/md-file-converter/templates/unsupported-object.md +6 -0
- package/.github/skills/mockAPI-contract-generator/SKILL.md +607 -0
- package/.github/skills/test-case-validator/CHANGELOG.md +155 -0
- package/.github/skills/test-case-validator/CONFIGURATION.md +130 -0
- package/.github/skills/test-case-validator/EXAMPLES.md +50 -0
- package/.github/skills/test-case-validator/QUALITY_SCORING.md +98 -0
- package/.github/skills/test-case-validator/README.md +111 -0
- package/.github/skills/test-case-validator/REPORT_TEMPLATE.md +116 -0
- package/.github/skills/test-case-validator/SKILL.md +209 -0
- package/.github/skills/test-case-validator/SYSTEM_PROMPT.md +127 -0
- package/.github/skills/test-case-validator/TRACEABILITY_ENGINE.md +152 -0
- package/.github/skills/test-case-validator/VALIDATION_RULES.md +411 -0
- package/.github/skills/test-case-validator/config/default.config.yaml +62 -0
- package/.github/skills/test-case-validator/docs/ARCHITECTURE.md +66 -0
- package/.github/skills/test-case-validator/docs/EXTENDING.md +85 -0
- package/.github/skills/test-case-validator/docs/FAQ.md +88 -0
- package/.github/skills/test-case-validator/docs/VERSIONING.md +55 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-brd.md +43 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-test-cases.csv +9 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/input-user-story.md +29 -0
- package/.github/skills/test-case-validator/examples/example-1-manual-qa-ecommerce/output-report.md +303 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-spec.md +43 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/input-test-files-index.md +56 -0
- package/.github/skills/test-case-validator/examples/example-2-automated-frontend-suite/output-report.md +274 -0
- package/.github/skills/test-case-validator/prompts/00-applicability-resolution.md +61 -0
- package/.github/skills/test-case-validator/prompts/01-extraction-user-story.md +58 -0
- package/.github/skills/test-case-validator/prompts/02-extraction-brd.md +64 -0
- package/.github/skills/test-case-validator/prompts/03-extraction-test-cases.md +92 -0
- package/.github/skills/test-case-validator/prompts/04-traceability-matrix.md +41 -0
- package/.github/skills/test-case-validator/prompts/05-coverage-analysis.md +46 -0
- package/.github/skills/test-case-validator/prompts/06-missing-scenario-detection.md +32 -0
- package/.github/skills/test-case-validator/prompts/07-duplicate-detection.md +36 -0
- package/.github/skills/test-case-validator/prompts/08-risk-analysis.md +39 -0
- package/.github/skills/test-case-validator/prompts/09-gated-wiring-validation.md +53 -0
- package/.github/skills/test-case-validator/prompts/10-observation-report.md +56 -0
- package/.github/skills/test-case-validator/prompts/11-final-report-assembly.md +57 -0
- package/.github/skills/test-case-validator/templates/coverage-table.md +25 -0
- package/.github/skills/test-case-validator/templates/executive-summary.md +38 -0
- package/.github/skills/test-case-validator/templates/final-summary-block.md +87 -0
- package/.github/skills/test-case-validator/templates/observation-entry.md +41 -0
- package/.github/skills/test-case-validator/templates/rtm-row.md +25 -0
- package/.github/skills/validation-engine-builder/README.md +50 -0
- package/.github/skills/validation-engine-builder/SKILL.md +241 -0
- package/.github/skills/validation-engine-builder/references/core.md +158 -0
- package/.github/skills/validation-engine-builder/references/platform-angular.md +166 -0
- package/.github/skills/validation-engine-builder/references/platform-flutter.md +118 -0
- package/.github/skills/validation-engine-builder/references/platform-react-native.md +170 -0
- package/.github/skills/validation-engine-builder/references/platform-react.md +94 -0
- package/.github/skills/validation-engine-builder/scripts/apply-to-project.js +80 -0
- package/.github/skills/validation-engine-builder/scripts/generate-angular.js +271 -0
- package/.github/skills/validation-engine-builder/scripts/generate-flutter.js +298 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react-native.js +217 -0
- package/.github/skills/validation-engine-builder/scripts/generate-react.js +163 -0
- package/.github/skills/validation-engine-builder/scripts/install-deps.js +53 -0
- package/.github/skills/validation-engine-builder/scripts/shared/detect-platform.js +68 -0
- package/.github/skills/validation-engine-builder/scripts/shared/npm-deps.js +103 -0
- package/.github/skills/validation-engine-builder/scripts/shared/rhf-hook.js +43 -0
- package/.github/skills/validation-engine-builder/scripts/shared/ts-messages-regex.js +62 -0
- package/.github/skills/validation-engine-builder/scripts/shared/util.js +44 -0
- package/.github/skills/validation-engine-builder/scripts/shared/yup-schemas.js +80 -0
- package/.github/skills/validation-engine-builder/template.md +73 -0
- package/.github/skills/validation-engine-builder/templates/messages.json +22 -0
- package/.github/skills/validation-engine-builder/templates/regex.json +16 -0
- package/.github/skills/validation-engine-builder/templates/schemas.json +48 -0
- package/bin/index.js +145 -0
- package/package.json +22 -0
- package/readme.md +0 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
# RSA + Native — Implementation Reference
|
|
2
|
+
|
|
3
|
+
Covers the **RSA model + Native layer** only.
|
|
4
|
+
The interceptor structure, request mapping, and envelope shape are identical to RSA JS mode.
|
|
5
|
+
Only the AES and RSA operations move into platform-native code.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Architecture Overview
|
|
10
|
+
|
|
11
|
+
- AES-256-GCM and RSA-OAEP run inside the device OS engine.
|
|
12
|
+
- Android: `javax.crypto.Cipher` (AES/GCM/NoPadding + RSA/ECB/OAEPWithSHA-256AndMGF1Padding).
|
|
13
|
+
- iOS: `CryptoKit.AES.GCM.seal/open` + `SecKeyCreateEncryptedData(.rsaEncryptionOAEPSHA256)`.
|
|
14
|
+
- Per-request AES keys are generated in JS with `randomBytes(32)`, passed as base64 into every native call, and discarded after the response. They are never stored in Android Keystore or iOS Secure Enclave.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Step 1: Dependencies
|
|
19
|
+
|
|
20
|
+
Install these for the native crypto backend. Keep `react-native-quick-crypto` — it is still required for `randomBytes(32)` used in the JS interceptor to generate per-request AES keys before passing them into native calls.
|
|
21
|
+
|
|
22
|
+
```
|
|
23
|
+
react-native-quick-crypto — still required for randomBytes (key generation in JS interceptor)
|
|
24
|
+
react-native-aes-crypto — native AES-256-GCM (Android javax.crypto + iOS CryptoKit/CommonCrypto)
|
|
25
|
+
react-native-rsa-native — native RSA-OAEP (Android KeyFactory + iOS SecKeyCreateEncryptedData)
|
|
26
|
+
react-native-keychain — token/session storage (unchanged from JS mode)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
- Android: verify `minSdkVersion >= 23` before proceeding.
|
|
30
|
+
- iOS: verify deployment target `>= 13.0` for CryptoKit; plan CommonCrypto fallback if below.
|
|
31
|
+
- Run `pod install` (iOS) and Gradle sync (Android) after installation.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Step 2: Android — `NativeCryptoModule.kt`
|
|
36
|
+
|
|
37
|
+
```kotlin
|
|
38
|
+
// android/app/src/main/java/.../NativeCryptoModule.kt
|
|
39
|
+
package com.yourapp
|
|
40
|
+
|
|
41
|
+
import android.util.Base64
|
|
42
|
+
import com.facebook.react.bridge.*
|
|
43
|
+
import java.security.KeyFactory
|
|
44
|
+
import java.security.spec.X509EncodedKeySpec
|
|
45
|
+
import javax.crypto.Cipher
|
|
46
|
+
import javax.crypto.spec.GCMParameterSpec
|
|
47
|
+
import javax.crypto.spec.SecretKeySpec
|
|
48
|
+
|
|
49
|
+
class NativeCryptoModule(reactContext: ReactApplicationContext) :
|
|
50
|
+
ReactContextBaseJavaModule(reactContext) {
|
|
51
|
+
|
|
52
|
+
override fun getName() = "NativeCryptoModule"
|
|
53
|
+
|
|
54
|
+
@ReactMethod
|
|
55
|
+
fun encryptAES(plaintext: String, keyBase64: String, promise: Promise) {
|
|
56
|
+
try {
|
|
57
|
+
val keyBytes = Base64.decode(keyBase64, Base64.NO_WRAP)
|
|
58
|
+
require(keyBytes.size == 32) { "AES key must be 32 bytes" }
|
|
59
|
+
val secretKey = SecretKeySpec(keyBytes, "AES")
|
|
60
|
+
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
|
61
|
+
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
|
|
62
|
+
val iv = cipher.iv // 12 bytes, auto-generated
|
|
63
|
+
val cipherWithTag = cipher.doFinal(plaintext.toByteArray(Charsets.UTF_8))
|
|
64
|
+
// GCM appends the 16-byte auth tag to cipherWithTag automatically
|
|
65
|
+
val result = WritableNativeMap()
|
|
66
|
+
result.putString("ciphertext", Base64.encodeToString(cipherWithTag, Base64.NO_WRAP))
|
|
67
|
+
result.putString("iv", Base64.encodeToString(iv, Base64.NO_WRAP))
|
|
68
|
+
promise.resolve(result)
|
|
69
|
+
} catch (e: Exception) {
|
|
70
|
+
promise.reject("ERR_ENCRYPT", e.message, e)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
@ReactMethod
|
|
75
|
+
fun decryptAES(ciphertextBase64: String, ivBase64: String, keyBase64: String, promise: Promise) {
|
|
76
|
+
try {
|
|
77
|
+
val keyBytes = Base64.decode(keyBase64, Base64.NO_WRAP)
|
|
78
|
+
require(keyBytes.size == 32) { "AES key must be 32 bytes" }
|
|
79
|
+
val cipherWithTag = Base64.decode(ciphertextBase64, Base64.NO_WRAP)
|
|
80
|
+
val iv = Base64.decode(ivBase64, Base64.NO_WRAP)
|
|
81
|
+
val secretKey = SecretKeySpec(keyBytes, "AES")
|
|
82
|
+
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
|
83
|
+
cipher.init(Cipher.DECRYPT_MODE, secretKey, GCMParameterSpec(128, iv))
|
|
84
|
+
// GCM verifies the auth tag automatically; throws AEADBadTagException on failure
|
|
85
|
+
val plaintext = cipher.doFinal(cipherWithTag)
|
|
86
|
+
promise.resolve(String(plaintext, Charsets.UTF_8))
|
|
87
|
+
} catch (e: Exception) {
|
|
88
|
+
promise.reject("ERR_DECRYPT", e.message, e)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
@ReactMethod
|
|
93
|
+
fun encryptRSA(publicKeyPem: String, dataBase64: String, promise: Promise) {
|
|
94
|
+
try {
|
|
95
|
+
val pem = publicKeyPem
|
|
96
|
+
.replace("-----BEGIN PUBLIC KEY-----", "")
|
|
97
|
+
.replace("-----END PUBLIC KEY-----", "")
|
|
98
|
+
.replace("\n", "").trim()
|
|
99
|
+
val keyBytes = Base64.decode(pem, Base64.NO_WRAP)
|
|
100
|
+
val publicKey = KeyFactory.getInstance("RSA").generatePublic(X509EncodedKeySpec(keyBytes))
|
|
101
|
+
val cipher = Cipher.getInstance("RSA/ECB/OAEPWithSHA-256AndMGF1Padding")
|
|
102
|
+
cipher.init(Cipher.ENCRYPT_MODE, publicKey)
|
|
103
|
+
val encrypted = cipher.doFinal(Base64.decode(dataBase64, Base64.NO_WRAP))
|
|
104
|
+
promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
|
|
105
|
+
} catch (e: Exception) {
|
|
106
|
+
promise.reject("ERR_RSA_ENCRYPT", e.message, e)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
> `AEADBadTagException` on decryption = auth tag failure → trigger force logout/session reset.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## Step 3: Android — `NativeCryptoPackage.kt` + Registration
|
|
117
|
+
|
|
118
|
+
```kotlin
|
|
119
|
+
// NativeCryptoPackage.kt
|
|
120
|
+
class NativeCryptoPackage : ReactPackage {
|
|
121
|
+
override fun createNativeModules(context: ReactApplicationContext) =
|
|
122
|
+
listOf(NativeCryptoModule(context))
|
|
123
|
+
override fun createViewManagers(context: ReactApplicationContext) = emptyList<ViewManager<*, *>>()
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Register in `MainApplication.kt` (or `.java`):
|
|
128
|
+
|
|
129
|
+
```kotlin
|
|
130
|
+
override fun getPackages(): List<ReactPackage> = listOf(
|
|
131
|
+
MainReactPackage(),
|
|
132
|
+
NativeCryptoPackage(), // ← add this
|
|
133
|
+
)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## Step 4: iOS — `NativeCryptoModule.swift`
|
|
139
|
+
|
|
140
|
+
**Never use `try!` in production code. Always use `do/try/catch`.**
|
|
141
|
+
|
|
142
|
+
```swift
|
|
143
|
+
// ios/NativeCryptoModule.swift
|
|
144
|
+
import CryptoKit
|
|
145
|
+
import Foundation
|
|
146
|
+
|
|
147
|
+
@objc(NativeCryptoModule)
|
|
148
|
+
class NativeCryptoModule: NSObject {
|
|
149
|
+
|
|
150
|
+
@objc func encryptAES(
|
|
151
|
+
_ plaintext: String,
|
|
152
|
+
keyBase64: String,
|
|
153
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
154
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
155
|
+
) {
|
|
156
|
+
do {
|
|
157
|
+
guard let data = plaintext.data(using: .utf8),
|
|
158
|
+
let keyData = Data(base64Encoded: keyBase64),
|
|
159
|
+
keyData.count == 32 else {
|
|
160
|
+
return reject("ERR_INVALID_INPUT", "Invalid plaintext or key", nil)
|
|
161
|
+
}
|
|
162
|
+
let symmetricKey = SymmetricKey(data: keyData)
|
|
163
|
+
let nonce = try AES.GCM.Nonce()
|
|
164
|
+
let sealed = try AES.GCM.seal(data, using: symmetricKey, nonce: nonce)
|
|
165
|
+
resolve([
|
|
166
|
+
"ciphertext": (sealed.ciphertext + sealed.tag).base64EncodedString(),
|
|
167
|
+
"iv": Data(nonce).base64EncodedString()
|
|
168
|
+
])
|
|
169
|
+
} catch {
|
|
170
|
+
reject("ERR_ENCRYPT", error.localizedDescription, error)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@objc func decryptAES(
|
|
175
|
+
_ ciphertextBase64: String,
|
|
176
|
+
ivBase64: String,
|
|
177
|
+
keyBase64: String,
|
|
178
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
179
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
180
|
+
) {
|
|
181
|
+
do {
|
|
182
|
+
guard let combined = Data(base64Encoded: ciphertextBase64),
|
|
183
|
+
let ivData = Data(base64Encoded: ivBase64),
|
|
184
|
+
let keyData = Data(base64Encoded: keyBase64),
|
|
185
|
+
keyData.count == 32,
|
|
186
|
+
combined.count > 16 else {
|
|
187
|
+
return reject("ERR_INVALID_INPUT", "Invalid decrypt inputs", nil)
|
|
188
|
+
}
|
|
189
|
+
let tag = combined.suffix(16)
|
|
190
|
+
let ciphertext = combined.dropLast(16)
|
|
191
|
+
let symmetricKey = SymmetricKey(data: keyData)
|
|
192
|
+
let nonce = try AES.GCM.Nonce(data: ivData)
|
|
193
|
+
let box = try AES.GCM.SealedBox(nonce: nonce, ciphertext: ciphertext, tag: tag)
|
|
194
|
+
let plainData = try AES.GCM.open(box, using: symmetricKey)
|
|
195
|
+
guard let result = String(data: plainData, encoding: .utf8) else {
|
|
196
|
+
return reject("ERR_DECODE", "Failed to decode plaintext", nil)
|
|
197
|
+
}
|
|
198
|
+
resolve(result)
|
|
199
|
+
} catch {
|
|
200
|
+
reject("ERR_DECRYPT", error.localizedDescription, error)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@objc func encryptRSA(
|
|
205
|
+
_ publicKeyPEM: String,
|
|
206
|
+
dataBase64: String,
|
|
207
|
+
resolve: @escaping RCTPromiseResolveBlock,
|
|
208
|
+
reject: @escaping RCTPromiseRejectBlock
|
|
209
|
+
) {
|
|
210
|
+
guard let data = Data(base64Encoded: dataBase64) else {
|
|
211
|
+
return reject("ERR_INVALID_INPUT", "Invalid data for RSA encrypt", nil)
|
|
212
|
+
}
|
|
213
|
+
let pemStripped = publicKeyPEM
|
|
214
|
+
.replacingOccurrences(of: "-----BEGIN PUBLIC KEY-----", with: "")
|
|
215
|
+
.replacingOccurrences(of: "-----END PUBLIC KEY-----", with: "")
|
|
216
|
+
.replacingOccurrences(of: "\n", with: "")
|
|
217
|
+
guard let keyData = Data(base64Encoded: pemStripped) else {
|
|
218
|
+
return reject("ERR_KEY_PARSE", "Invalid PEM key", nil)
|
|
219
|
+
}
|
|
220
|
+
let keyDict: [CFString: Any] = [
|
|
221
|
+
kSecAttrKeyType: kSecAttrKeyTypeRSA,
|
|
222
|
+
kSecAttrKeyClass: kSecAttrKeyClassPublic
|
|
223
|
+
]
|
|
224
|
+
guard let secKey = SecKeyCreateWithData(keyData as CFData, keyDict as CFDictionary, nil) else {
|
|
225
|
+
return reject("ERR_KEY_LOAD", "Failed to load RSA public key", nil)
|
|
226
|
+
}
|
|
227
|
+
var error: Unmanaged<CFError>?
|
|
228
|
+
guard let encrypted = SecKeyCreateEncryptedData(
|
|
229
|
+
secKey, .rsaEncryptionOAEPSHA256, data as CFData, &error
|
|
230
|
+
) else {
|
|
231
|
+
reject("ERR_RSA_ENCRYPT",
|
|
232
|
+
error?.takeRetainedValue().localizedDescription ?? "RSA encrypt failed", nil)
|
|
233
|
+
return
|
|
234
|
+
}
|
|
235
|
+
resolve((encrypted as Data).base64EncodedString())
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
> iOS < 13 fallback: use `CommonCrypto CCCrypt` with `kCCAlgorithmAES` / GCM via `CCCryptorGCM`. Prefer CryptoKit for all new work.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Step 5: iOS — Bridge File (`NativeCryptoModule.m`)
|
|
245
|
+
|
|
246
|
+
```objc
|
|
247
|
+
// ios/NativeCryptoModule.m
|
|
248
|
+
#import <React/RCTBridgeModule.h>
|
|
249
|
+
|
|
250
|
+
@interface RCT_EXTERN_MODULE(NativeCryptoModule, NSObject)
|
|
251
|
+
|
|
252
|
+
RCT_EXTERN_METHOD(encryptAES:(NSString *)plaintext
|
|
253
|
+
keyBase64:(NSString *)keyBase64
|
|
254
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
255
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
256
|
+
|
|
257
|
+
RCT_EXTERN_METHOD(decryptAES:(NSString *)ciphertextBase64
|
|
258
|
+
ivBase64:(NSString *)ivBase64
|
|
259
|
+
keyBase64:(NSString *)keyBase64
|
|
260
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
261
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
262
|
+
|
|
263
|
+
RCT_EXTERN_METHOD(encryptRSA:(NSString *)publicKeyPEM
|
|
264
|
+
dataBase64:(NSString *)dataBase64
|
|
265
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
266
|
+
reject:(RCTPromiseRejectBlock)reject)
|
|
267
|
+
|
|
268
|
+
@end
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
Add `NativeCryptoModule.m` to the Xcode project target. Run `pod install`.
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
## Step 6: JS Bridge Interface (`native-crypto.module.ts`)
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// src/services/security/native-crypto.module.ts
|
|
279
|
+
import { NativeModules } from 'react-native';
|
|
280
|
+
|
|
281
|
+
const { NativeCryptoModule } = NativeModules;
|
|
282
|
+
|
|
283
|
+
export interface NativeEncryptResult {
|
|
284
|
+
ciphertext: string; // base64(ciphertext + 16-byte auth_tag)
|
|
285
|
+
iv: string; // base64(12-byte IV)
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export const nativeEncryptAES = (
|
|
289
|
+
plaintext: string,
|
|
290
|
+
keyBase64: string,
|
|
291
|
+
): Promise<NativeEncryptResult> =>
|
|
292
|
+
NativeCryptoModule.encryptAES(plaintext, keyBase64);
|
|
293
|
+
|
|
294
|
+
export const nativeDecryptAES = (
|
|
295
|
+
ciphertextBase64: string,
|
|
296
|
+
ivBase64: string,
|
|
297
|
+
keyBase64: string,
|
|
298
|
+
): Promise<string> =>
|
|
299
|
+
NativeCryptoModule.decryptAES(ciphertextBase64, ivBase64, keyBase64);
|
|
300
|
+
|
|
301
|
+
export const nativeEncryptRSA = (
|
|
302
|
+
publicKeyPEM: string,
|
|
303
|
+
dataBase64: string,
|
|
304
|
+
): Promise<string> =>
|
|
305
|
+
NativeCryptoModule.encryptRSA(publicKeyPEM, dataBase64);
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Step 7: Interceptor Integration (RSA + Native)
|
|
311
|
+
|
|
312
|
+
```typescript
|
|
313
|
+
// Request interceptor — RSA Native path
|
|
314
|
+
import { randomBytes } from 'react-native-quick-crypto';
|
|
315
|
+
import { nativeEncryptAES, nativeEncryptRSA } from './native-crypto.module';
|
|
316
|
+
|
|
317
|
+
const aesKeyBytes = randomBytes(32);
|
|
318
|
+
const aesKeyBase64 = aesKeyBytes.toString('base64');
|
|
319
|
+
|
|
320
|
+
const { ciphertext, iv } = await nativeEncryptAES(JSON.stringify(requestBody), aesKeyBase64);
|
|
321
|
+
const encryptedKey = await nativeEncryptRSA(rsaPublicKeyPEM, aesKeyBase64);
|
|
322
|
+
|
|
323
|
+
// Store only the AES key — response decryption uses responseData.iv (server-generated IV), not request iv.
|
|
324
|
+
requestMap.set(requestId, { aesKeyBase64 });
|
|
325
|
+
config.data = { data: ciphertext, key: encryptedKey, iv, nonce, key_id: currentKeyId };
|
|
326
|
+
|
|
327
|
+
// Response decryption:
|
|
328
|
+
const { aesKeyBase64 } = requestMap.get(requestId);
|
|
329
|
+
const plaintext = await nativeDecryptAES(responseData.data, responseData.iv, aesKeyBase64);
|
|
330
|
+
requestMap.delete(requestId); // clean up immediately
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Step 8: Environment Variables
|
|
336
|
+
|
|
337
|
+
```
|
|
338
|
+
ENCRYPTION_ENABLED=true
|
|
339
|
+
ENCRYPTION_MODEL=rsa
|
|
340
|
+
ENCRYPTION_LAYER=native
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Deliverables Summary
|
|
346
|
+
|
|
347
|
+
| File | Action |
|
|
348
|
+
|------|--------|
|
|
349
|
+
| `android/app/src/main/java/.../NativeCryptoModule.kt` | Create |
|
|
350
|
+
| `android/app/src/main/java/.../NativeCryptoPackage.kt` | Create |
|
|
351
|
+
| `android/app/src/main/java/.../MainApplication.kt` | Register `NativeCryptoPackage` |
|
|
352
|
+
| `ios/NativeCryptoModule.swift` | Create |
|
|
353
|
+
| `ios/NativeCryptoModule.m` | Create, add to Xcode target |
|
|
354
|
+
| `src/services/security/native-crypto.module.ts` | Create JS bridge |
|
|
355
|
+
| `src/services/api.ts` | Update interceptors to call native bridge |
|
|
356
|
+
| `.env` / `.env.example` | Add `ENCRYPTION_LAYER=native` |
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Startup Validation
|
|
361
|
+
|
|
362
|
+
Before the first encrypted request, verify the native module is accessible:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
import { NativeModules } from 'react-native';
|
|
366
|
+
|
|
367
|
+
if (!NativeModules.NativeCryptoModule) {
|
|
368
|
+
throw new Error(
|
|
369
|
+
'NativeCryptoModule not found. Ensure NativeCryptoPackage is registered in MainApplication ' +
|
|
370
|
+
'(Android) and NativeCryptoModule.m is linked in Xcode (iOS). Run pod install and clean build.'
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Platform: Node.js (Express/NestJS)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Node.js uses an **Express middleware** function to transparently encrypt outgoing response bodies
|
|
6
|
+
or decrypt incoming request bodies depending on the use-case.
|
|
7
|
+
Supports Approaches 2 and 3 only (Approach 1 requires browser Web Crypto and is NOT compatible).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## File Naming Convention
|
|
12
|
+
|
|
13
|
+
| Role | Path |
|
|
14
|
+
|----------------|--------------------------------------------------|
|
|
15
|
+
| Config | `src/encryption/encryption.config.ts` |
|
|
16
|
+
| Service | `src/encryption/encryption.service.ts` |
|
|
17
|
+
| Interceptor | `src/encryption/encryption.middleware.ts` |
|
|
18
|
+
| Module wiring | `src/main.ts` |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Module Wiring (Express)
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import { encryptionMiddleware } from './encryption/encryption.middleware';
|
|
26
|
+
|
|
27
|
+
app.use(express.json());
|
|
28
|
+
app.use(encryptionMiddleware);
|
|
29
|
+
// ENCRYPTION_INTERCEPTOR_PLACEHOLDER
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
The `// ENCRYPTION_INTERCEPTOR_PLACEHOLDER` comment is the marker used by install.js `patchFile()`.
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Middleware Pattern
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
export async function encryptionMiddleware(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
40
|
+
const cfg = encryptionConfig.encryption;
|
|
41
|
+
if (!cfg.enabled || req.body == null) return next();
|
|
42
|
+
if (cfg.excludeUrls.some(prefix => req.path.startsWith(prefix))) return next();
|
|
43
|
+
try {
|
|
44
|
+
req.body = await encryptionService.encrypt(req.body);
|
|
45
|
+
} catch {}
|
|
46
|
+
next();
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Built-in crypto (Approach 3 — no extra libraries)
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import crypto from 'crypto';
|
|
56
|
+
|
|
57
|
+
function encrypt(body: object, sharedKey: string): object {
|
|
58
|
+
const key = crypto.createHash('sha256').update(sharedKey).digest();
|
|
59
|
+
const iv = crypto.randomBytes(16);
|
|
60
|
+
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
|
|
61
|
+
const encrypted = Buffer.concat([cipher.update(JSON.stringify(body), 'utf8'), cipher.final()]);
|
|
62
|
+
return {
|
|
63
|
+
iv: iv.toString('base64'),
|
|
64
|
+
encryptedData: encrypted.toString('base64'),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Dependencies (Approach 2)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install jsencrypt crypto-js
|
|
75
|
+
npm install --save-dev @types/crypto-js
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Approach 3 uses the built-in `crypto` module — no extra packages.
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## Build Command
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run build
|
|
86
|
+
```
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Platform: React (Web / CRA / Vite)
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
React projects use an **Axios interceptor** to transparently encrypt outgoing request bodies.
|
|
6
|
+
All encryption logic lives in `EncryptionService`; the interceptor calls the service and attaches the encrypted payload before the request leaves the client.
|
|
7
|
+
Supports Approaches 1, 2, 3.
|
|
8
|
+
|
|
9
|
+
> **Approach 7 — React JS Web Production Mode**: For production-grade encryption using the built-in Web Crypto API (AES-256-GCM + RSA-OAEP or ECDH-Session, per-request ephemeral keys, response decryption, browser-safe storage, zero extra crypto packages), see `references/react-web-crypto.md`. Approach 7 supersedes approach 1 for new React JS deployments requiring a full request/response encryption lifecycle.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## File Naming Convention
|
|
14
|
+
|
|
15
|
+
| Role | Path |
|
|
16
|
+
|----------------|-------------------------------------------------------|
|
|
17
|
+
| Config | `src/encryption/encryption.config.ts` |
|
|
18
|
+
| Service | `src/encryption/encryption.service.ts` |
|
|
19
|
+
| Interceptor | `src/encryption/encryption.interceptor.ts` |
|
|
20
|
+
| Module wiring | `src/api/axiosInstance.ts` |
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Module Wiring
|
|
25
|
+
|
|
26
|
+
Create a shared Axios instance in `src/api/axiosInstance.ts` and register the interceptor there.
|
|
27
|
+
Import this instance in all API call sites instead of importing `axios` directly.
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import axios from 'axios';
|
|
31
|
+
import { applyEncryptionInterceptor } from '../encryption/encryption.interceptor';
|
|
32
|
+
|
|
33
|
+
const axiosInstance = axios.create({
|
|
34
|
+
baseURL: encryptionConfig.apiUrl,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
applyEncryptionInterceptor(axiosInstance);
|
|
38
|
+
// ENCRYPTION_INTERCEPTOR_PLACEHOLDER
|
|
39
|
+
|
|
40
|
+
export default axiosInstance;
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The `// ENCRYPTION_INTERCEPTOR_PLACEHOLDER` comment is the marker used by install.js `patchFile()`.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Interceptor Pattern
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';
|
|
51
|
+
import { encryptionService } from './encryption.service';
|
|
52
|
+
import { encryptionConfig } from './encryption.config';
|
|
53
|
+
|
|
54
|
+
export function applyEncryptionInterceptor(instance: AxiosInstance): void {
|
|
55
|
+
const cfg = encryptionConfig.encryption;
|
|
56
|
+
|
|
57
|
+
instance.interceptors.request.use(
|
|
58
|
+
async (config: InternalAxiosRequestConfig) => {
|
|
59
|
+
if (!cfg.enabled) return config;
|
|
60
|
+
if (!config.data) return config;
|
|
61
|
+
if (config.data instanceof FormData) return config;
|
|
62
|
+
if (cfg.excludeUrls?.some(u => config.url?.startsWith(u))) return config;
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
config.data = await encryptionService.encrypt(config.data);
|
|
66
|
+
config.headers['Content-Type'] = 'application/json';
|
|
67
|
+
} catch {}
|
|
68
|
+
|
|
69
|
+
return config;
|
|
70
|
+
},
|
|
71
|
+
(error) => Promise.reject(error)
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## URL Exclusion Pattern
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
const excludeUrls: string[] = encryptionConfig.encryption.excludeUrls;
|
|
82
|
+
if (excludeUrls.some(prefix => config.url?.startsWith(prefix))) {
|
|
83
|
+
return config;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Dependencies (Approach 2 or 3)
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npm install axios jsencrypt crypto-js
|
|
93
|
+
npm install --save-dev @types/crypto-js
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Approach 1 uses the browser-native `crypto.subtle` — no extra packages.
|
|
97
|
+
Axios is required for all approaches when using the interceptor pattern.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Build Command
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm run build
|
|
105
|
+
```
|