@striae-org/striae 4.3.4 → 5.1.0
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/.env.example +9 -2
- package/app/components/actions/case-export/download-handlers.ts +66 -11
- package/app/components/actions/case-import/confirmation-import.ts +50 -7
- package/app/components/actions/case-import/confirmation-package.ts +99 -22
- package/app/components/actions/case-import/orchestrator.ts +116 -13
- package/app/components/actions/case-import/validation.ts +171 -7
- package/app/components/actions/case-import/zip-processing.ts +224 -127
- package/app/components/actions/case-manage.ts +74 -15
- package/app/components/actions/confirm-export.ts +32 -3
- package/app/components/actions/generate-pdf.ts +43 -1
- package/app/components/actions/image-manage.ts +13 -45
- package/app/components/navbar/navbar.module.css +0 -10
- package/app/components/navbar/navbar.tsx +0 -22
- package/app/components/sidebar/case-import/case-import.module.css +7 -131
- package/app/components/sidebar/case-import/case-import.tsx +7 -14
- package/app/components/sidebar/case-import/components/CasePreviewSection.tsx +17 -60
- package/app/components/sidebar/case-import/components/ConfirmationDialog.tsx +23 -39
- package/app/components/sidebar/case-import/components/ConfirmationPreviewSection.tsx +5 -45
- package/app/components/sidebar/case-import/components/FileSelector.tsx +5 -6
- package/app/components/sidebar/case-import/hooks/useFilePreview.ts +2 -48
- package/app/components/sidebar/case-import/utils/file-validation.ts +9 -21
- package/app/config-example/config.json +5 -0
- package/app/routes/auth/login.tsx +1 -1
- package/app/routes/striae/hooks/use-striae-reset-helpers.ts +4 -0
- package/app/routes/striae/striae.tsx +15 -4
- package/app/utils/data/operations/case-operations.ts +13 -1
- package/app/utils/data/operations/confirmation-summary-operations.ts +38 -1
- package/app/utils/data/operations/file-annotation-operations.ts +13 -1
- package/app/utils/data/operations/signing-operations.ts +93 -0
- package/app/utils/data/operations/types.ts +6 -0
- package/app/utils/forensics/export-encryption.ts +316 -0
- package/app/utils/forensics/export-verification.ts +1 -409
- package/app/utils/forensics/index.ts +1 -0
- package/app/utils/ui/case-messages.ts +5 -2
- package/package.json +2 -2
- package/scripts/deploy-config.sh +244 -7
- package/scripts/deploy-pages-secrets.sh +0 -6
- package/scripts/deploy-worker-secrets.sh +66 -5
- package/scripts/encrypt-r2-backfill.mjs +376 -0
- package/worker-configuration.d.ts +13 -7
- package/workers/audit-worker/package.json +1 -4
- package/workers/audit-worker/src/audit-worker.example.ts +522 -61
- package/workers/audit-worker/wrangler.jsonc.example +6 -1
- package/workers/data-worker/package.json +1 -4
- package/workers/data-worker/src/data-worker.example.ts +409 -1
- package/workers/data-worker/src/encryption-utils.ts +269 -0
- package/workers/data-worker/worker-configuration.d.ts +1 -1
- package/workers/data-worker/wrangler.jsonc.example +6 -2
- package/workers/image-worker/package.json +1 -4
- package/workers/image-worker/src/encryption-utils.ts +217 -0
- package/workers/image-worker/src/image-worker.example.ts +196 -127
- package/workers/image-worker/wrangler.jsonc.example +8 -1
- package/workers/keys-worker/package.json +1 -4
- package/workers/keys-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/package.json +1 -4
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/package.json +1 -4
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
- package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +0 -287
- package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +0 -470
package/scripts/deploy-config.sh
CHANGED
|
@@ -334,7 +334,7 @@ write_env_var() {
|
|
|
334
334
|
var_value=$(strip_carriage_returns "$var_value")
|
|
335
335
|
env_file_value="$var_value"
|
|
336
336
|
|
|
337
|
-
if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PUBLIC_KEY" ]; then
|
|
337
|
+
if [ "$var_name" = "FIREBASE_SERVICE_ACCOUNT_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PRIVATE_KEY" ] || [ "$var_name" = "MANIFEST_SIGNING_PUBLIC_KEY" ] || [ "$var_name" = "EXPORT_ENCRYPTION_PRIVATE_KEY" ] || [ "$var_name" = "EXPORT_ENCRYPTION_PUBLIC_KEY" ] || [ "$var_name" = "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" ] || [ "$var_name" = "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" ]; then
|
|
338
338
|
# Store as a quoted string so sourced .env preserves escaped newline markers (\n)
|
|
339
339
|
env_file_value=${env_file_value//\"/\\\"}
|
|
340
340
|
env_file_value="\"$env_file_value\""
|
|
@@ -508,6 +508,229 @@ configure_manifest_signing_credentials() {
|
|
|
508
508
|
echo ""
|
|
509
509
|
}
|
|
510
510
|
|
|
511
|
+
generate_export_encryption_key_pair() {
|
|
512
|
+
local private_key_file
|
|
513
|
+
local public_key_file
|
|
514
|
+
private_key_file=$(mktemp)
|
|
515
|
+
public_key_file=$(mktemp)
|
|
516
|
+
|
|
517
|
+
if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
|
|
518
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
519
|
+
return 1
|
|
520
|
+
fi
|
|
521
|
+
|
|
522
|
+
local private_key_pem
|
|
523
|
+
local public_key_pem
|
|
524
|
+
private_key_pem=$(cat "$private_key_file")
|
|
525
|
+
public_key_pem=$(cat "$public_key_file")
|
|
526
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
527
|
+
|
|
528
|
+
private_key_pem="${private_key_pem//$'\r'/}"
|
|
529
|
+
public_key_pem="${public_key_pem//$'\r'/}"
|
|
530
|
+
|
|
531
|
+
EXPORT_ENCRYPTION_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
|
|
532
|
+
EXPORT_ENCRYPTION_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
|
|
533
|
+
|
|
534
|
+
export EXPORT_ENCRYPTION_PRIVATE_KEY
|
|
535
|
+
export EXPORT_ENCRYPTION_PUBLIC_KEY
|
|
536
|
+
|
|
537
|
+
write_env_var "EXPORT_ENCRYPTION_PRIVATE_KEY" "$EXPORT_ENCRYPTION_PRIVATE_KEY"
|
|
538
|
+
write_env_var "EXPORT_ENCRYPTION_PUBLIC_KEY" "$EXPORT_ENCRYPTION_PUBLIC_KEY"
|
|
539
|
+
|
|
540
|
+
return 0
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
configure_export_encryption_credentials() {
|
|
544
|
+
echo -e "${BLUE}🔐 EXPORT ENCRYPTION CONFIGURATION${NC}"
|
|
545
|
+
echo "================================="
|
|
546
|
+
|
|
547
|
+
local should_generate="false"
|
|
548
|
+
local regenerate_choice=""
|
|
549
|
+
|
|
550
|
+
if [ "$update_env" = "true" ]; then
|
|
551
|
+
should_generate="true"
|
|
552
|
+
elif [ -z "$EXPORT_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$EXPORT_ENCRYPTION_PRIVATE_KEY" || [ -z "$EXPORT_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$EXPORT_ENCRYPTION_PUBLIC_KEY"; then
|
|
553
|
+
should_generate="true"
|
|
554
|
+
else
|
|
555
|
+
echo -e "${GREEN}Current export encryption key pair: [HIDDEN]${NC}"
|
|
556
|
+
read -p "Generate new export encryption key pair? (press Enter to keep current, or type 'y' to regenerate): " regenerate_choice
|
|
557
|
+
regenerate_choice=$(strip_carriage_returns "$regenerate_choice")
|
|
558
|
+
if [ "$regenerate_choice" = "y" ] || [ "$regenerate_choice" = "Y" ]; then
|
|
559
|
+
should_generate="true"
|
|
560
|
+
fi
|
|
561
|
+
fi
|
|
562
|
+
|
|
563
|
+
if [ "$should_generate" = "true" ]; then
|
|
564
|
+
echo -e "${YELLOW}Generating export encryption RSA key pair...${NC}"
|
|
565
|
+
if generate_export_encryption_key_pair; then
|
|
566
|
+
echo -e "${GREEN}✅ Export encryption key pair generated${NC}"
|
|
567
|
+
else
|
|
568
|
+
echo -e "${RED}❌ Error: Failed to generate export encryption key pair${NC}"
|
|
569
|
+
exit 1
|
|
570
|
+
fi
|
|
571
|
+
else
|
|
572
|
+
echo -e "${GREEN}✅ Keeping current export encryption key pair${NC}"
|
|
573
|
+
fi
|
|
574
|
+
|
|
575
|
+
if [ -z "$EXPORT_ENCRYPTION_KEY_ID" ] || is_placeholder "$EXPORT_ENCRYPTION_KEY_ID" || [ "$should_generate" = "true" ]; then
|
|
576
|
+
local generated_key_id
|
|
577
|
+
generated_key_id=$(generate_worker_subdomain_label)
|
|
578
|
+
if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
|
|
579
|
+
echo -e "${RED}❌ Error: Failed to generate EXPORT_ENCRYPTION_KEY_ID${NC}"
|
|
580
|
+
exit 1
|
|
581
|
+
fi
|
|
582
|
+
EXPORT_ENCRYPTION_KEY_ID="$generated_key_id"
|
|
583
|
+
export EXPORT_ENCRYPTION_KEY_ID
|
|
584
|
+
write_env_var "EXPORT_ENCRYPTION_KEY_ID" "$EXPORT_ENCRYPTION_KEY_ID"
|
|
585
|
+
echo -e "${GREEN}✅ EXPORT_ENCRYPTION_KEY_ID generated: $EXPORT_ENCRYPTION_KEY_ID${NC}"
|
|
586
|
+
else
|
|
587
|
+
echo -e "${GREEN}✅ EXPORT_ENCRYPTION_KEY_ID: $EXPORT_ENCRYPTION_KEY_ID${NC}"
|
|
588
|
+
fi
|
|
589
|
+
|
|
590
|
+
echo ""
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
generate_data_at_rest_encryption_key_pair() {
|
|
594
|
+
local private_key_file
|
|
595
|
+
local public_key_file
|
|
596
|
+
private_key_file=$(mktemp)
|
|
597
|
+
public_key_file=$(mktemp)
|
|
598
|
+
|
|
599
|
+
if ! node -e "const { generateKeyPairSync } = require('crypto'); const fs = require('fs'); const pair = generateKeyPairSync('rsa', { modulusLength: 2048, publicKeyEncoding: { type: 'spki', format: 'pem' }, privateKeyEncoding: { type: 'pkcs8', format: 'pem' } }); fs.writeFileSync(process.argv[1], pair.privateKey, 'utf8'); fs.writeFileSync(process.argv[2], pair.publicKey, 'utf8');" "$private_key_file" "$public_key_file"; then
|
|
600
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
601
|
+
return 1
|
|
602
|
+
fi
|
|
603
|
+
|
|
604
|
+
local private_key_pem
|
|
605
|
+
local public_key_pem
|
|
606
|
+
private_key_pem=$(cat "$private_key_file")
|
|
607
|
+
public_key_pem=$(cat "$public_key_file")
|
|
608
|
+
rm -f "$private_key_file" "$public_key_file"
|
|
609
|
+
|
|
610
|
+
private_key_pem="${private_key_pem//$'\r'/}"
|
|
611
|
+
public_key_pem="${public_key_pem//$'\r'/}"
|
|
612
|
+
|
|
613
|
+
DATA_AT_REST_ENCRYPTION_PRIVATE_KEY="${private_key_pem//$'\n'/\\n}"
|
|
614
|
+
DATA_AT_REST_ENCRYPTION_PUBLIC_KEY="${public_key_pem//$'\n'/\\n}"
|
|
615
|
+
|
|
616
|
+
export DATA_AT_REST_ENCRYPTION_PRIVATE_KEY
|
|
617
|
+
export DATA_AT_REST_ENCRYPTION_PUBLIC_KEY
|
|
618
|
+
|
|
619
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY"
|
|
620
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"
|
|
621
|
+
|
|
622
|
+
return 0
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
configure_data_at_rest_encryption_credentials() {
|
|
626
|
+
echo -e "${BLUE}🗃️ DATA-AT-REST ENCRYPTION CONFIGURATION${NC}"
|
|
627
|
+
echo "========================================"
|
|
628
|
+
|
|
629
|
+
local current_enabled="${DATA_AT_REST_ENCRYPTION_ENABLED:-}"
|
|
630
|
+
local normalized_enabled=""
|
|
631
|
+
local enable_choice=""
|
|
632
|
+
|
|
633
|
+
current_enabled=$(strip_carriage_returns "$current_enabled")
|
|
634
|
+
normalized_enabled=$(printf '%s' "$current_enabled" | tr '[:upper:]' '[:lower:]')
|
|
635
|
+
|
|
636
|
+
if [ -z "$normalized_enabled" ] || is_placeholder "$normalized_enabled"; then
|
|
637
|
+
DATA_AT_REST_ENCRYPTION_ENABLED="false"
|
|
638
|
+
elif [ "$normalized_enabled" = "1" ] || [ "$normalized_enabled" = "true" ] || [ "$normalized_enabled" = "yes" ] || [ "$normalized_enabled" = "on" ]; then
|
|
639
|
+
DATA_AT_REST_ENCRYPTION_ENABLED="true"
|
|
640
|
+
else
|
|
641
|
+
DATA_AT_REST_ENCRYPTION_ENABLED="false"
|
|
642
|
+
fi
|
|
643
|
+
|
|
644
|
+
if [ "$update_env" != "true" ]; then
|
|
645
|
+
read -p "Enable data-at-rest encryption for new writes? (y/N, Enter keeps current): " enable_choice
|
|
646
|
+
enable_choice=$(strip_carriage_returns "$enable_choice")
|
|
647
|
+
case "$enable_choice" in
|
|
648
|
+
y|Y|yes|YES)
|
|
649
|
+
DATA_AT_REST_ENCRYPTION_ENABLED="true"
|
|
650
|
+
;;
|
|
651
|
+
n|N|no|NO)
|
|
652
|
+
DATA_AT_REST_ENCRYPTION_ENABLED="false"
|
|
653
|
+
;;
|
|
654
|
+
"")
|
|
655
|
+
;;
|
|
656
|
+
*)
|
|
657
|
+
echo -e "${YELLOW}⚠️ Unrecognized choice '$enable_choice'; keeping current setting${NC}"
|
|
658
|
+
;;
|
|
659
|
+
esac
|
|
660
|
+
fi
|
|
661
|
+
|
|
662
|
+
export DATA_AT_REST_ENCRYPTION_ENABLED
|
|
663
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_ENABLED" "$DATA_AT_REST_ENCRYPTION_ENABLED"
|
|
664
|
+
echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_ENABLED: $DATA_AT_REST_ENCRYPTION_ENABLED${NC}"
|
|
665
|
+
|
|
666
|
+
local should_generate="false"
|
|
667
|
+
local regenerate_choice=""
|
|
668
|
+
|
|
669
|
+
if [ "$update_env" = "true" ]; then
|
|
670
|
+
should_generate="true"
|
|
671
|
+
elif [ -z "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" || [ -z "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"; then
|
|
672
|
+
should_generate="true"
|
|
673
|
+
else
|
|
674
|
+
echo -e "${GREEN}Current data-at-rest encryption key pair: [HIDDEN]${NC}"
|
|
675
|
+
read -p "Generate new data-at-rest encryption key pair? (press Enter to keep current, or type 'y' to regenerate): " regenerate_choice
|
|
676
|
+
regenerate_choice=$(strip_carriage_returns "$regenerate_choice")
|
|
677
|
+
if [ "$regenerate_choice" = "y" ] || [ "$regenerate_choice" = "Y" ]; then
|
|
678
|
+
should_generate="true"
|
|
679
|
+
fi
|
|
680
|
+
fi
|
|
681
|
+
|
|
682
|
+
if [ "$should_generate" = "true" ]; then
|
|
683
|
+
echo -e "${YELLOW}Generating data-at-rest encryption RSA key pair...${NC}"
|
|
684
|
+
if generate_data_at_rest_encryption_key_pair; then
|
|
685
|
+
echo -e "${GREEN}✅ Data-at-rest encryption key pair generated${NC}"
|
|
686
|
+
else
|
|
687
|
+
echo -e "${RED}❌ Error: Failed to generate data-at-rest encryption key pair${NC}"
|
|
688
|
+
exit 1
|
|
689
|
+
fi
|
|
690
|
+
else
|
|
691
|
+
echo -e "${GREEN}✅ Keeping current data-at-rest encryption key pair${NC}"
|
|
692
|
+
fi
|
|
693
|
+
|
|
694
|
+
if [ -z "$DATA_AT_REST_ENCRYPTION_KEY_ID" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_KEY_ID" || [ "$should_generate" = "true" ]; then
|
|
695
|
+
local generated_key_id
|
|
696
|
+
generated_key_id=$(generate_worker_subdomain_label)
|
|
697
|
+
if [ -z "$generated_key_id" ] || [ ${#generated_key_id} -ne 10 ]; then
|
|
698
|
+
echo -e "${RED}❌ Error: Failed to generate DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
|
|
699
|
+
exit 1
|
|
700
|
+
fi
|
|
701
|
+
DATA_AT_REST_ENCRYPTION_KEY_ID="$generated_key_id"
|
|
702
|
+
export DATA_AT_REST_ENCRYPTION_KEY_ID
|
|
703
|
+
write_env_var "DATA_AT_REST_ENCRYPTION_KEY_ID" "$DATA_AT_REST_ENCRYPTION_KEY_ID"
|
|
704
|
+
echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_KEY_ID generated: $DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
|
|
705
|
+
else
|
|
706
|
+
echo -e "${GREEN}✅ DATA_AT_REST_ENCRYPTION_KEY_ID: $DATA_AT_REST_ENCRYPTION_KEY_ID${NC}"
|
|
707
|
+
fi
|
|
708
|
+
|
|
709
|
+
echo ""
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
validate_data_at_rest_encryption_settings() {
|
|
713
|
+
local enabled_normalized
|
|
714
|
+
enabled_normalized=$(printf '%s' "${DATA_AT_REST_ENCRYPTION_ENABLED:-false}" | tr '[:upper:]' '[:lower:]')
|
|
715
|
+
|
|
716
|
+
if [ "$enabled_normalized" = "1" ] || [ "$enabled_normalized" = "true" ] || [ "$enabled_normalized" = "yes" ] || [ "$enabled_normalized" = "on" ]; then
|
|
717
|
+
if [ -z "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PRIVATE_KEY"; then
|
|
718
|
+
echo -e "${RED}❌ Error: DATA_AT_REST_ENCRYPTION_PRIVATE_KEY is required when DATA_AT_REST_ENCRYPTION_ENABLED is true${NC}"
|
|
719
|
+
exit 1
|
|
720
|
+
fi
|
|
721
|
+
|
|
722
|
+
if [ -z "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_PUBLIC_KEY"; then
|
|
723
|
+
echo -e "${RED}❌ Error: DATA_AT_REST_ENCRYPTION_PUBLIC_KEY is required when DATA_AT_REST_ENCRYPTION_ENABLED is true${NC}"
|
|
724
|
+
exit 1
|
|
725
|
+
fi
|
|
726
|
+
|
|
727
|
+
if [ -z "$DATA_AT_REST_ENCRYPTION_KEY_ID" ] || is_placeholder "$DATA_AT_REST_ENCRYPTION_KEY_ID"; then
|
|
728
|
+
echo -e "${RED}❌ Error: DATA_AT_REST_ENCRYPTION_KEY_ID is required when DATA_AT_REST_ENCRYPTION_ENABLED is true${NC}"
|
|
729
|
+
exit 1
|
|
730
|
+
fi
|
|
731
|
+
fi
|
|
732
|
+
}
|
|
733
|
+
|
|
511
734
|
# Validate required variables
|
|
512
735
|
required_vars=(
|
|
513
736
|
# Core Cloudflare Configuration
|
|
@@ -552,18 +775,20 @@ required_vars=(
|
|
|
552
775
|
# Storage Configuration (required for config replacement)
|
|
553
776
|
"DATA_BUCKET_NAME"
|
|
554
777
|
"AUDIT_BUCKET_NAME"
|
|
778
|
+
"FILES_BUCKET_NAME"
|
|
555
779
|
"KV_STORE_ID"
|
|
556
780
|
|
|
557
781
|
# Worker-Specific Secrets (required for deployment)
|
|
558
782
|
"KEYS_AUTH"
|
|
559
783
|
"PDF_WORKER_AUTH"
|
|
560
784
|
"ACCOUNT_HASH"
|
|
561
|
-
"API_TOKEN"
|
|
562
785
|
"BROWSER_API_TOKEN"
|
|
563
|
-
"HMAC_KEY"
|
|
564
786
|
"MANIFEST_SIGNING_PRIVATE_KEY"
|
|
565
787
|
"MANIFEST_SIGNING_KEY_ID"
|
|
566
788
|
"MANIFEST_SIGNING_PUBLIC_KEY"
|
|
789
|
+
"EXPORT_ENCRYPTION_PRIVATE_KEY"
|
|
790
|
+
"EXPORT_ENCRYPTION_KEY_ID"
|
|
791
|
+
"EXPORT_ENCRYPTION_PUBLIC_KEY"
|
|
567
792
|
)
|
|
568
793
|
|
|
569
794
|
validate_required_vars() {
|
|
@@ -591,7 +816,7 @@ assert_contains_literal() {
|
|
|
591
816
|
local literal=$2
|
|
592
817
|
local description=$3
|
|
593
818
|
|
|
594
|
-
if ! grep -Fq "$literal" "$file_path"; then
|
|
819
|
+
if ! grep -Fq -- "$literal" "$file_path"; then
|
|
595
820
|
echo -e "${RED}❌ Error: ${description}${NC}"
|
|
596
821
|
echo -e "${YELLOW} Expected to find '$literal' in $file_path${NC}"
|
|
597
822
|
exit 1
|
|
@@ -739,10 +964,13 @@ validate_generated_configs() {
|
|
|
739
964
|
|
|
740
965
|
assert_contains_literal "workers/data-worker/wrangler.jsonc" "$DATA_BUCKET_NAME" "DATA_BUCKET_NAME missing in data worker config"
|
|
741
966
|
assert_contains_literal "workers/audit-worker/wrangler.jsonc" "$AUDIT_BUCKET_NAME" "AUDIT_BUCKET_NAME missing in audit worker config"
|
|
967
|
+
assert_contains_literal "workers/image-worker/wrangler.jsonc" "$FILES_BUCKET_NAME" "FILES_BUCKET_NAME missing in image worker config"
|
|
742
968
|
assert_contains_literal "workers/user-worker/wrangler.jsonc" "$KV_STORE_ID" "KV_STORE_ID missing in user worker config"
|
|
743
969
|
|
|
744
970
|
assert_contains_literal "app/config/config.json" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in app/config/config.json"
|
|
745
971
|
assert_contains_literal "app/config/config.json" "$ACCOUNT_HASH" "ACCOUNT_HASH missing in app/config/config.json"
|
|
972
|
+
assert_contains_literal "app/config/config.json" "$EXPORT_ENCRYPTION_KEY_ID" "EXPORT_ENCRYPTION_KEY_ID missing in app/config/config.json"
|
|
973
|
+
assert_contains_literal "app/config/config.json" "\"export_encryption_public_key\":" "export_encryption_public_key missing in app/config/config.json"
|
|
746
974
|
assert_contains_literal "app/routes/auth/login.tsx" "const APP_CANONICAL_ORIGIN = 'https://$PAGES_CUSTOM_DOMAIN';" "PAGES_CUSTOM_DOMAIN missing in app/routes/auth/login.tsx canonical origin"
|
|
747
975
|
|
|
748
976
|
assert_contains_literal "app/config/firebase.ts" "$API_KEY" "API_KEY missing in app/config/firebase.ts"
|
|
@@ -761,7 +989,7 @@ validate_generated_configs() {
|
|
|
761
989
|
assert_contains_literal "workers/user-worker/src/user-worker.ts" "https://$PAGES_CUSTOM_DOMAIN" "PAGES_CUSTOM_DOMAIN missing in user-worker source"
|
|
762
990
|
|
|
763
991
|
local placeholder_pattern
|
|
764
|
-
placeholder_pattern="(\"(ACCOUNT_ID|PAGES_PROJECT_NAME|PAGES_CUSTOM_DOMAIN|KEYS_WORKER_NAME|USER_WORKER_NAME|DATA_WORKER_NAME|AUDIT_WORKER_NAME|IMAGES_WORKER_NAME|PDF_WORKER_NAME|KEYS_WORKER_DOMAIN|USER_WORKER_DOMAIN|DATA_WORKER_DOMAIN|AUDIT_WORKER_DOMAIN|IMAGES_WORKER_DOMAIN|PDF_WORKER_DOMAIN|DATA_BUCKET_NAME|AUDIT_BUCKET_NAME|KV_STORE_ID|ACCOUNT_HASH|MANIFEST_SIGNING_KEY_ID|MANIFEST_SIGNING_PUBLIC_KEY|YOUR_FIREBASE_API_KEY|YOUR_FIREBASE_AUTH_DOMAIN|YOUR_FIREBASE_PROJECT_ID|YOUR_FIREBASE_STORAGE_BUCKET|YOUR_FIREBASE_MESSAGING_SENDER_ID|YOUR_FIREBASE_APP_ID|YOUR_FIREBASE_MEASUREMENT_ID)\"|'(PAGES_CUSTOM_DOMAIN|DATA_WORKER_DOMAIN|IMAGES_WORKER_DOMAIN)')"
|
|
992
|
+
placeholder_pattern="(\"(ACCOUNT_ID|PAGES_PROJECT_NAME|PAGES_CUSTOM_DOMAIN|KEYS_WORKER_NAME|USER_WORKER_NAME|DATA_WORKER_NAME|AUDIT_WORKER_NAME|IMAGES_WORKER_NAME|PDF_WORKER_NAME|KEYS_WORKER_DOMAIN|USER_WORKER_DOMAIN|DATA_WORKER_DOMAIN|AUDIT_WORKER_DOMAIN|IMAGES_WORKER_DOMAIN|PDF_WORKER_DOMAIN|DATA_BUCKET_NAME|AUDIT_BUCKET_NAME|FILES_BUCKET_NAME|KV_STORE_ID|ACCOUNT_HASH|MANIFEST_SIGNING_KEY_ID|MANIFEST_SIGNING_PUBLIC_KEY|EXPORT_ENCRYPTION_KEY_ID|EXPORT_ENCRYPTION_PUBLIC_KEY|YOUR_FIREBASE_API_KEY|YOUR_FIREBASE_AUTH_DOMAIN|YOUR_FIREBASE_PROJECT_ID|YOUR_FIREBASE_STORAGE_BUCKET|YOUR_FIREBASE_MESSAGING_SENDER_ID|YOUR_FIREBASE_APP_ID|YOUR_FIREBASE_MEASUREMENT_ID)\"|'(PAGES_CUSTOM_DOMAIN|DATA_WORKER_DOMAIN|IMAGES_WORKER_DOMAIN)')"
|
|
765
993
|
|
|
766
994
|
local files_to_scan=(
|
|
767
995
|
"wrangler.toml"
|
|
@@ -793,6 +1021,7 @@ run_validation_checkpoint() {
|
|
|
793
1021
|
validate_required_vars
|
|
794
1022
|
validate_env_value_formats
|
|
795
1023
|
validate_env_file_entries
|
|
1024
|
+
validate_data_at_rest_encryption_settings
|
|
796
1025
|
validate_generated_configs
|
|
797
1026
|
}
|
|
798
1027
|
|
|
@@ -1298,6 +1527,7 @@ prompt_for_secrets() {
|
|
|
1298
1527
|
echo "========================="
|
|
1299
1528
|
prompt_for_var "DATA_BUCKET_NAME" "Your R2 bucket name for case data storage"
|
|
1300
1529
|
prompt_for_var "AUDIT_BUCKET_NAME" "Your R2 bucket name for audit logs (separate from data bucket)"
|
|
1530
|
+
prompt_for_var "FILES_BUCKET_NAME" "Your R2 bucket name for encrypted files storage"
|
|
1301
1531
|
prompt_for_var "KV_STORE_ID" "Your KV namespace ID (UUID format)"
|
|
1302
1532
|
|
|
1303
1533
|
echo -e "${BLUE}🔐 SERVICE-SPECIFIC SECRETS${NC}"
|
|
@@ -1305,11 +1535,11 @@ prompt_for_secrets() {
|
|
|
1305
1535
|
prompt_for_var "KEYS_AUTH" "Keys worker authentication token (generate with: openssl rand -hex 16)"
|
|
1306
1536
|
prompt_for_var "PDF_WORKER_AUTH" "PDF worker authentication token (generate with: openssl rand -hex 16)"
|
|
1307
1537
|
prompt_for_var "ACCOUNT_HASH" "Cloudflare Images Account Hash"
|
|
1308
|
-
prompt_for_var "API_TOKEN" "Cloudflare Images API token (for Images Worker)"
|
|
1309
1538
|
prompt_for_var "BROWSER_API_TOKEN" "Cloudflare Browser Rendering API token (for PDF Worker)"
|
|
1310
|
-
prompt_for_var "HMAC_KEY" "Cloudflare Images HMAC signing key"
|
|
1311
1539
|
|
|
1312
1540
|
configure_manifest_signing_credentials
|
|
1541
|
+
configure_export_encryption_credentials
|
|
1542
|
+
configure_data_at_rest_encryption_credentials
|
|
1313
1543
|
|
|
1314
1544
|
# Reload the updated .env file
|
|
1315
1545
|
source .env
|
|
@@ -1374,6 +1604,7 @@ update_wrangler_configs() {
|
|
|
1374
1604
|
echo -e "${YELLOW} Updating image-worker/wrangler.jsonc...${NC}"
|
|
1375
1605
|
sed -i "s/\"IMAGES_WORKER_NAME\"/\"$IMAGES_WORKER_NAME\"/g" workers/image-worker/wrangler.jsonc
|
|
1376
1606
|
sed -i "s/\"ACCOUNT_ID\"/\"$ACCOUNT_ID\"/g" workers/image-worker/wrangler.jsonc
|
|
1607
|
+
sed -i "s/\"FILES_BUCKET_NAME\"/\"$FILES_BUCKET_NAME\"/g" workers/image-worker/wrangler.jsonc
|
|
1377
1608
|
echo -e "${GREEN} ✅ image-worker configuration updated${NC}"
|
|
1378
1609
|
fi
|
|
1379
1610
|
|
|
@@ -1447,15 +1678,21 @@ update_wrangler_configs() {
|
|
|
1447
1678
|
echo -e "${YELLOW} Updating app/config/config.json...${NC}"
|
|
1448
1679
|
local escaped_manifest_signing_key_id
|
|
1449
1680
|
local escaped_manifest_signing_public_key
|
|
1681
|
+
local escaped_export_encryption_key_id
|
|
1682
|
+
local escaped_export_encryption_public_key
|
|
1450
1683
|
local escaped_account_hash
|
|
1451
1684
|
escaped_manifest_signing_key_id=$(escape_for_sed_replacement "$MANIFEST_SIGNING_KEY_ID")
|
|
1452
1685
|
escaped_manifest_signing_public_key=$(escape_for_sed_replacement "$MANIFEST_SIGNING_PUBLIC_KEY")
|
|
1686
|
+
escaped_export_encryption_key_id=$(escape_for_sed_replacement "$EXPORT_ENCRYPTION_KEY_ID")
|
|
1687
|
+
escaped_export_encryption_public_key=$(escape_for_sed_replacement "$EXPORT_ENCRYPTION_PUBLIC_KEY")
|
|
1453
1688
|
escaped_account_hash=$(escape_for_sed_replacement "$ACCOUNT_HASH")
|
|
1454
1689
|
|
|
1455
1690
|
sed -i "s|\"url\": \"[^\"]*\"|\"url\": \"https://$escaped_pages_custom_domain\"|g" app/config/config.json
|
|
1456
1691
|
sed -i "s|\"account_hash\": \"[^\"]*\"|\"account_hash\": \"$escaped_account_hash\"|g" app/config/config.json
|
|
1457
1692
|
sed -i "s|\"MANIFEST_SIGNING_KEY_ID\"|\"$escaped_manifest_signing_key_id\"|g" app/config/config.json
|
|
1458
1693
|
sed -i "s|\"MANIFEST_SIGNING_PUBLIC_KEY\"|\"$escaped_manifest_signing_public_key\"|g" app/config/config.json
|
|
1694
|
+
sed -i "s|\"EXPORT_ENCRYPTION_KEY_ID\"|\"$escaped_export_encryption_key_id\"|g" app/config/config.json
|
|
1695
|
+
sed -i "s|\"EXPORT_ENCRYPTION_PUBLIC_KEY\"|\"$escaped_export_encryption_public_key\"|g" app/config/config.json
|
|
1459
1696
|
echo -e "${GREEN} ✅ app config.json updated${NC}"
|
|
1460
1697
|
fi
|
|
1461
1698
|
|
|
@@ -172,12 +172,6 @@ deploy_pages_environment_secrets() {
|
|
|
172
172
|
set_pages_secret "$secret" "$secret_value" "$pages_env"
|
|
173
173
|
done
|
|
174
174
|
|
|
175
|
-
local optional_api_token
|
|
176
|
-
optional_api_token=$(get_optional_value "API_TOKEN")
|
|
177
|
-
if [ -n "$optional_api_token" ]; then
|
|
178
|
-
set_pages_secret "API_TOKEN" "$optional_api_token" "$pages_env"
|
|
179
|
-
fi
|
|
180
|
-
|
|
181
175
|
local optional_primershear_emails
|
|
182
176
|
optional_primershear_emails=$(get_optional_value "PRIMERSHEAR_EMAILS")
|
|
183
177
|
if [ -n "$optional_primershear_emails" ]; then
|
|
@@ -95,6 +95,30 @@ load_required_admin_service_credentials() {
|
|
|
95
95
|
|
|
96
96
|
load_required_admin_service_credentials
|
|
97
97
|
|
|
98
|
+
build_audit_worker_secret_list() {
|
|
99
|
+
local secrets=(
|
|
100
|
+
"R2_KEY_SECRET"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_ENABLED:-}" ]; then
|
|
104
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_ENABLED")
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_PRIVATE_KEY:-}" ]; then
|
|
108
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_PRIVATE_KEY")
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_PUBLIC_KEY:-}" ]; then
|
|
112
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_PUBLIC_KEY")
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_KEY_ID:-}" ]; then
|
|
116
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_KEY_ID")
|
|
117
|
+
fi
|
|
118
|
+
|
|
119
|
+
printf '%s\n' "${secrets[@]}"
|
|
120
|
+
}
|
|
121
|
+
|
|
98
122
|
# Function to set worker secrets
|
|
99
123
|
set_worker_secrets() {
|
|
100
124
|
local worker_name=$1
|
|
@@ -143,6 +167,34 @@ set_worker_secrets() {
|
|
|
143
167
|
popd > /dev/null
|
|
144
168
|
}
|
|
145
169
|
|
|
170
|
+
build_data_worker_secret_list() {
|
|
171
|
+
local secrets=(
|
|
172
|
+
"R2_KEY_SECRET"
|
|
173
|
+
"MANIFEST_SIGNING_PRIVATE_KEY"
|
|
174
|
+
"MANIFEST_SIGNING_KEY_ID"
|
|
175
|
+
"EXPORT_ENCRYPTION_PRIVATE_KEY"
|
|
176
|
+
"EXPORT_ENCRYPTION_KEY_ID"
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_ENABLED:-}" ]; then
|
|
180
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_ENABLED")
|
|
181
|
+
fi
|
|
182
|
+
|
|
183
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_PRIVATE_KEY:-}" ]; then
|
|
184
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_PRIVATE_KEY")
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_PUBLIC_KEY:-}" ]; then
|
|
188
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_PUBLIC_KEY")
|
|
189
|
+
fi
|
|
190
|
+
|
|
191
|
+
if [ -n "${DATA_AT_REST_ENCRYPTION_KEY_ID:-}" ]; then
|
|
192
|
+
secrets+=("DATA_AT_REST_ENCRYPTION_KEY_ID")
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
printf '%s\n' "${secrets[@]}"
|
|
196
|
+
}
|
|
197
|
+
|
|
146
198
|
# Deploy secrets to each worker
|
|
147
199
|
echo -e "\n${BLUE}🔐 Deploying secrets to workers...${NC}"
|
|
148
200
|
|
|
@@ -168,8 +220,12 @@ elif [ $workers_configured -lt $total_workers ]; then
|
|
|
168
220
|
fi
|
|
169
221
|
|
|
170
222
|
# Audit Worker
|
|
171
|
-
|
|
172
|
-
|
|
223
|
+
audit_worker_secrets=()
|
|
224
|
+
while IFS= read -r secret; do
|
|
225
|
+
audit_worker_secrets+=("$secret")
|
|
226
|
+
done < <(build_audit_worker_secret_list)
|
|
227
|
+
|
|
228
|
+
if ! set_worker_secrets "Audit Worker" "workers/audit-worker" "${audit_worker_secrets[@]}"; then
|
|
173
229
|
echo -e "${YELLOW}⚠️ Skipping Audit Worker (not configured)${NC}"
|
|
174
230
|
fi
|
|
175
231
|
|
|
@@ -186,14 +242,18 @@ if ! set_worker_secrets "User Worker" "workers/user-worker" \
|
|
|
186
242
|
fi
|
|
187
243
|
|
|
188
244
|
# Data Worker
|
|
189
|
-
|
|
190
|
-
|
|
245
|
+
data_worker_secrets=()
|
|
246
|
+
while IFS= read -r secret; do
|
|
247
|
+
data_worker_secrets+=("$secret")
|
|
248
|
+
done < <(build_data_worker_secret_list)
|
|
249
|
+
|
|
250
|
+
if ! set_worker_secrets "Data Worker" "workers/data-worker" "${data_worker_secrets[@]}"; then
|
|
191
251
|
echo -e "${YELLOW}⚠️ Skipping Data Worker (not configured)${NC}"
|
|
192
252
|
fi
|
|
193
253
|
|
|
194
254
|
# Images Worker
|
|
195
255
|
if ! set_worker_secrets "Images Worker" "workers/image-worker" \
|
|
196
|
-
"
|
|
256
|
+
"IMAGES_API_TOKEN" "DATA_AT_REST_ENCRYPTION_PRIVATE_KEY" "DATA_AT_REST_ENCRYPTION_PUBLIC_KEY" "DATA_AT_REST_ENCRYPTION_KEY_ID"; then
|
|
197
257
|
echo -e "${YELLOW}⚠️ Skipping Images Worker (not configured)${NC}"
|
|
198
258
|
fi
|
|
199
259
|
|
|
@@ -210,6 +270,7 @@ echo " - Copy wrangler.jsonc.example to wrangler.jsonc in each worker director
|
|
|
210
270
|
echo " - Configure KV namespace ID in workers/user-worker/wrangler.jsonc"
|
|
211
271
|
echo " - Configure R2 bucket name in workers/data-worker/wrangler.jsonc"
|
|
212
272
|
echo " - Configure R2 bucket name in workers/audit-worker/wrangler.jsonc"
|
|
273
|
+
echo " - Configure R2 bucket name in workers/image-worker/wrangler.jsonc"
|
|
213
274
|
echo " - Update ACCOUNT_ID and custom domains in all worker configurations"
|
|
214
275
|
|
|
215
276
|
echo -e "\n${BLUE}📝 For manual deployment, use these commands:${NC}"
|