@striae-org/striae 4.0.0 → 4.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.
Files changed (99) hide show
  1. package/.env.example +1 -0
  2. package/README.md +1 -1
  3. package/app/components/actions/case-export/data-processing.ts +1 -1
  4. package/app/components/actions/case-export/download-handlers.ts +5 -4
  5. package/app/components/actions/case-export/metadata-helpers.ts +1 -1
  6. package/app/components/actions/case-import/confirmation-import.ts +1 -1
  7. package/app/components/actions/case-import/image-operations.ts +1 -1
  8. package/app/components/actions/case-import/orchestrator.ts +1 -1
  9. package/app/components/actions/case-import/storage-operations.ts +3 -3
  10. package/app/components/actions/case-import/validation.ts +3 -4
  11. package/app/components/actions/case-import/zip-processing.ts +1 -1
  12. package/app/components/actions/case-manage.ts +3 -5
  13. package/app/components/actions/confirm-export.ts +4 -5
  14. package/app/components/actions/generate-pdf.ts +1 -1
  15. package/app/components/actions/image-manage.ts +4 -4
  16. package/app/components/actions/notes-manage.ts +1 -1
  17. package/app/components/actions/signout.tsx +1 -1
  18. package/app/components/audit/user-audit-viewer.tsx +1 -1
  19. package/app/components/auth/auth-provider.tsx +1 -1
  20. package/app/components/auth/mfa-verification.tsx +1 -1
  21. package/app/components/button/button.tsx +1 -1
  22. package/app/components/canvas/box-annotations/box-annotations.tsx +1 -1
  23. package/app/components/canvas/confirmation/confirmation.tsx +1 -1
  24. package/app/components/icon/icon.tsx +1 -1
  25. package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +1 -1
  26. package/app/components/sidebar/case-export/case-export.tsx +1 -1
  27. package/app/components/sidebar/cases/case-sidebar.tsx +3 -3
  28. package/app/components/sidebar/cases/cases-modal.tsx +1 -1
  29. package/app/components/sidebar/files/files-modal.tsx +1 -1
  30. package/app/components/sidebar/notes/notes-sidebar.tsx +1 -1
  31. package/app/components/sidebar/sidebar-container.tsx +2 -17
  32. package/app/components/sidebar/sidebar.module.css +0 -29
  33. package/app/components/theme-provider/theme-provider.tsx +1 -1
  34. package/app/components/theme-provider/theme.ts +1 -1
  35. package/app/components/user/delete-account.tsx +1 -1
  36. package/app/components/user/manage-profile.tsx +1 -1
  37. package/app/components/user/mfa-phone-update.tsx +1 -1
  38. package/app/root.tsx +18 -51
  39. package/app/routes/auth/emailActionHandler.tsx +2 -3
  40. package/app/routes/auth/emailVerification.tsx +2 -2
  41. package/app/routes/auth/login.tsx +7 -9
  42. package/app/routes/auth/passwordReset.tsx +2 -2
  43. package/app/routes/striae/striae.tsx +2 -2
  44. package/app/services/audit/audit-export-signing.ts +2 -2
  45. package/app/services/audit/audit-export.service.ts +1 -2
  46. package/app/services/audit/audit.service.ts +1 -1
  47. package/app/services/firebase/index.ts +1 -1
  48. package/app/utils/api/index.ts +4 -0
  49. package/app/utils/auth/index.ts +5 -0
  50. package/app/utils/common/index.ts +3 -0
  51. package/app/utils/{version.ts → common/version.ts} +1 -1
  52. package/app/utils/{data-operations.ts → data/data-operations.ts} +4 -4
  53. package/app/utils/data/index.ts +2 -0
  54. package/app/utils/{permissions.ts → data/permissions.ts} +1 -1
  55. package/app/utils/forensics/index.ts +5 -0
  56. package/app/utils/ui/index.ts +2 -0
  57. package/functions/api/image/[[path]].ts +17 -1
  58. package/package.json +18 -20
  59. package/public/.well-known/keybase.txt +56 -0
  60. package/public/.well-known/security.txt +3 -4
  61. package/scripts/deploy-config.sh +178 -142
  62. package/scripts/deploy-worker-secrets.sh +1 -2
  63. package/worker-configuration.d.ts +7491 -11363
  64. package/workers/audit-worker/worker-configuration.d.ts +11323 -7448
  65. package/workers/audit-worker/wrangler.jsonc.example +1 -1
  66. package/workers/data-worker/worker-configuration.d.ts +11323 -7448
  67. package/workers/data-worker/wrangler.jsonc.example +1 -1
  68. package/workers/image-worker/src/image-worker.example.ts +10 -2
  69. package/workers/image-worker/worker-configuration.d.ts +11322 -7447
  70. package/workers/image-worker/wrangler.jsonc.example +1 -1
  71. package/workers/keys-worker/src/keys.ts +2 -1
  72. package/workers/keys-worker/worker-configuration.d.ts +11322 -7447
  73. package/workers/keys-worker/wrangler.jsonc.example +1 -1
  74. package/workers/pdf-worker/src/pdf-worker.example.ts +144 -39
  75. package/workers/pdf-worker/worker-configuration.d.ts +11323 -7448
  76. package/workers/pdf-worker/wrangler.jsonc.example +1 -1
  77. package/workers/user-worker/worker-configuration.d.ts +11323 -7448
  78. package/workers/user-worker/wrangler.jsonc.example +1 -1
  79. package/wrangler.toml.example +1 -1
  80. package/public/.well-known/publickey.info@striae.org.asc +0 -17
  81. package/public/oin-badge.png +0 -0
  82. /package/app/utils/{data-api-client.ts → api/data-api-client.ts} +0 -0
  83. /package/app/utils/{image-api-client.ts → api/image-api-client.ts} +0 -0
  84. /package/app/utils/{pdf-api-client.ts → api/pdf-api-client.ts} +0 -0
  85. /package/app/utils/{user-api-client.ts → api/user-api-client.ts} +0 -0
  86. /package/app/utils/{auth-action-settings.ts → auth/auth-action-settings.ts} +0 -0
  87. /package/app/utils/{auth.ts → auth/auth.ts} +0 -0
  88. /package/app/utils/{mfa-phone.ts → auth/mfa-phone.ts} +0 -0
  89. /package/app/utils/{mfa.ts → auth/mfa.ts} +0 -0
  90. /package/app/utils/{password-policy.ts → auth/password-policy.ts} +0 -0
  91. /package/app/utils/{batch-operations.ts → common/batch-operations.ts} +0 -0
  92. /package/app/utils/{id-generator.ts → common/id-generator.ts} +0 -0
  93. /package/app/utils/{SHA256.ts → forensics/SHA256.ts} +0 -0
  94. /package/app/utils/{audit-export-signature.ts → forensics/audit-export-signature.ts} +0 -0
  95. /package/app/utils/{confirmation-signature.ts → forensics/confirmation-signature.ts} +0 -0
  96. /package/app/utils/{export-verification.ts → forensics/export-verification.ts} +0 -0
  97. /package/app/utils/{signature-utils.ts → forensics/signature-utils.ts} +0 -0
  98. /package/app/utils/{annotation-timestamp.ts → ui/annotation-timestamp.ts} +0 -0
  99. /package/app/utils/{style.ts → ui/style.ts} +0 -0
@@ -22,14 +22,12 @@ import { Toast } from '~/components/toast/toast';
22
22
  import { Icon } from '~/components/icon/icon';
23
23
  import styles from './login.module.css';
24
24
  import { Striae } from '~/routes/striae/striae';
25
- import { getUserData, createUser } from '~/utils/permissions';
25
+ import { getUserData, createUser } from '~/utils/data';
26
26
  import { auditService } from '~/services/audit';
27
- import { generateUniqueId } from '~/utils/id-generator';
28
- import { evaluatePasswordPolicy } from '~/utils/password-policy';
29
- import { buildActionCodeSettings } from '~/utils/auth-action-settings';
30
- import { userHasMFA } from '~/utils/mfa';
27
+ import { generateUniqueId } from '~/utils/common';
28
+ import { evaluatePasswordPolicy, buildActionCodeSettings, userHasMFA } from '~/utils/auth';
31
29
 
32
- const APP_CANONICAL_ORIGIN = 'https://app.striae.org';
30
+ const APP_CANONICAL_ORIGIN = 'https://striae.app';
33
31
  const SOCIAL_IMAGE_PATH = '/social-image.png';
34
32
  const SOCIAL_IMAGE_ALT = 'Striae forensic annotation and comparison workspace';
35
33
  const LOGIN_PATH_ALIASES = new Set(['/auth', '/auth/', '/auth/login', '/auth/login/']);
@@ -51,8 +49,8 @@ const getCanonicalPath = (pathname: string): string => {
51
49
  const getAuthMetaContent = (mode: string | null, hasActionCode: boolean): AuthMetaContent => {
52
50
  if (!mode && !hasActionCode) {
53
51
  return {
54
- title: 'Striae | Secure Login for Firearms Examiners',
55
- description: 'Sign in to Striae to access your forensic annotation workspace, case files, and comparison tools.',
52
+ title: 'Striae: A Firearms Examiner\'s Comparison Companion',
53
+ description: 'Sign in to Striae to access your comparison annotation workspace, case files, and review tools.',
56
54
  robots: 'index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1',
57
55
  };
58
56
  }
@@ -582,7 +580,7 @@ export const Login = () => {
582
580
  <Link
583
581
  viewTransition
584
582
  prefetch="intent"
585
- to="https://striae.org"
583
+ to="https://striae.app"
586
584
  className={styles.logoLink}>
587
585
  <div className={styles.logo} />
588
586
  </Link>
@@ -4,7 +4,7 @@ import { sendPasswordResetEmail, signOut } from 'firebase/auth';
4
4
  import { auth } from '~/services/firebase';
5
5
  import { handleAuthError, ERROR_MESSAGES } from '~/services/firebase/errors';
6
6
  import { auditService } from '~/services/audit';
7
- import { buildActionCodeSettings } from '~/utils/auth-action-settings';
7
+ import { buildActionCodeSettings } from '~/utils/auth';
8
8
  import styles from './passwordReset.module.css';
9
9
 
10
10
  interface PasswordResetProps {
@@ -120,7 +120,7 @@ export const PasswordReset = ({ isModal, onBack }: PasswordResetProps) => {
120
120
  <Link
121
121
  viewTransition
122
122
  prefetch="intent"
123
- to="https://striae.org"
123
+ to="https://striae.app"
124
124
  >
125
125
  <div className={styles.logo} />
126
126
  </Link>
@@ -7,8 +7,8 @@ import { Toast } from '~/components/toast/toast';
7
7
  import { getImageUrl } from '~/components/actions/image-manage';
8
8
  import { getNotes, saveNotes } from '~/components/actions/notes-manage';
9
9
  import { generatePDF } from '~/components/actions/generate-pdf';
10
- import { fetchUserApi } from '~/utils/user-api-client';
11
- import { resolveEarliestAnnotationTimestamp } from '~/utils/annotation-timestamp';
10
+ import { fetchUserApi } from '~/utils/api';
11
+ import { resolveEarliestAnnotationTimestamp } from '~/utils/ui';
12
12
  import { type AnnotationData, type FileData } from '~/types';
13
13
  import { checkCaseIsReadOnly } from '~/components/actions/case-manage';
14
14
  import styles from './striae.module.css';
@@ -1,11 +1,11 @@
1
1
  import type { User } from 'firebase/auth';
2
- import { signAuditExportData } from '~/utils/data-operations';
2
+ import { signAuditExportData } from '~/utils/data';
3
3
  import {
4
4
  AUDIT_EXPORT_SIGNATURE_VERSION,
5
5
  type AuditExportFormat,
6
6
  type AuditExportSigningPayload,
7
7
  type AuditExportType
8
- } from '~/utils/audit-export-signature';
8
+ } from '~/utils/forensics';
9
9
 
10
10
  export interface AuditExportContext {
11
11
  user: User;
@@ -1,6 +1,5 @@
1
1
  import { type ValidationAuditEntry, type AuditTrail } from '~/types';
2
- import { calculateSHA256Secure } from '~/utils/SHA256';
3
- import { type AuditExportType } from '~/utils/audit-export-signature';
2
+ import { calculateSHA256Secure, type AuditExportType } from '~/utils/forensics';
4
3
  import { AUDIT_CSV_ENTRY_HEADERS, entryToCSVRow } from './audit-export-csv';
5
4
  import { buildAuditReportContent } from './audit-export-report';
6
5
  import { type AuditExportContext, signAuditExport } from './audit-export-signing';
@@ -9,7 +9,7 @@ import type {
9
9
  AuditResult,
10
10
  PerformanceMetrics
11
11
  } from '~/types';
12
- import { generateWorkflowId } from '../../utils/id-generator';
12
+ import { generateWorkflowId } from '~/utils/common';
13
13
  import {
14
14
  fetchAuditEntriesForUser,
15
15
  persistAuditEntryForUser
@@ -6,7 +6,7 @@ import {
6
6
  //connectAuthEmulator,
7
7
  } from 'firebase/auth';
8
8
  import firebaseConfig from '~/config/firebase';
9
- import { getAppVersion } from '~/utils/version';
9
+ import { getAppVersion } from '~/utils/common';
10
10
 
11
11
  export const app = initializeApp(firebaseConfig, "Striae");
12
12
  export const auth = getAuth(app);
@@ -0,0 +1,4 @@
1
+ export * from './data-api-client';
2
+ export * from './image-api-client';
3
+ export * from './pdf-api-client';
4
+ export * from './user-api-client';
@@ -0,0 +1,5 @@
1
+ export * from './auth';
2
+ export * from './auth-action-settings';
3
+ export * from './mfa';
4
+ export * from './mfa-phone';
5
+ export * from './password-policy';
@@ -0,0 +1,3 @@
1
+ export * from './batch-operations';
2
+ export * from './id-generator';
3
+ export * from './version';
@@ -1,4 +1,4 @@
1
- import packageJson from '../../package.json';
1
+ import packageJson from '../../../package.json';
2
2
 
3
3
  export const getAppVersion = () => {
4
4
  return packageJson.version;
@@ -6,19 +6,19 @@
6
6
 
7
7
  import type { User } from 'firebase/auth';
8
8
  import { type CaseData, type AnnotationData, type ConfirmationImportData } from '~/types';
9
- import { fetchDataApi } from './data-api-client';
9
+ import { fetchDataApi } from '../api';
10
10
  import { validateUserSession, canAccessCase, canModifyCase } from './permissions';
11
11
  import {
12
12
  type ForensicManifestData,
13
13
  type ForensicManifestSignature,
14
14
  FORENSIC_MANIFEST_VERSION
15
- } from './SHA256';
16
- import { CONFIRMATION_SIGNATURE_VERSION } from './confirmation-signature';
15
+ } from '../forensics/SHA256';
16
+ import { CONFIRMATION_SIGNATURE_VERSION } from '../forensics/confirmation-signature';
17
17
  import {
18
18
  AUDIT_EXPORT_SIGNATURE_VERSION,
19
19
  type AuditExportSigningPayload,
20
20
  isValidAuditExportSigningPayload
21
- } from './audit-export-signature';
21
+ } from '../forensics/audit-export-signature';
22
22
 
23
23
  // ============================================================================
24
24
  // INTERFACES AND TYPES
@@ -0,0 +1,2 @@
1
+ export * from './data-operations';
2
+ export * from './permissions';
@@ -1,7 +1,7 @@
1
1
  import type { User } from 'firebase/auth';
2
2
  import type { UserData, ExtendedUserData, UserLimits, ReadOnlyCaseMetadata } from '~/types';
3
3
  import paths from '~/config/config.json';
4
- import { fetchUserApi } from './user-api-client';
4
+ import { fetchUserApi } from '../api';
5
5
 
6
6
  const MAX_CASES_REVIEW = paths.max_cases_review;
7
7
  const MAX_FILES_PER_CASE_REVIEW = paths.max_files_per_case_review;
@@ -0,0 +1,5 @@
1
+ export * from './SHA256';
2
+ export * from './audit-export-signature';
3
+ export * from './confirmation-signature';
4
+ export * from './export-verification';
5
+ export * from './signature-utils';
@@ -0,0 +1,2 @@
1
+ export * from './annotation-timestamp';
2
+ export * from './style';
@@ -37,7 +37,23 @@ function extractProxyPath(url: URL): string | null {
37
37
  }
38
38
 
39
39
  const remainder = url.pathname.slice(routePrefix.length);
40
- return remainder.length > 0 ? remainder : '/';
40
+ if (remainder.length === 0) {
41
+ return '/';
42
+ }
43
+
44
+ const normalizedRemainder = remainder.startsWith('/') ? remainder : `/${remainder}`;
45
+ const encodedPath = normalizedRemainder.slice(1);
46
+
47
+ try {
48
+ const decodedPath = decodeURIComponent(encodedPath);
49
+ if (decodedPath.length > 0) {
50
+ return decodedPath.startsWith('/') ? decodedPath : `/${decodedPath}`;
51
+ }
52
+ } catch {
53
+ // Keep legacy behavior for non-encoded paths.
54
+ }
55
+
56
+ return normalizedRemainder;
41
57
  }
42
58
 
43
59
  function resolveImageWorkerToken(env: Env): string {
package/package.json CHANGED
@@ -1,20 +1,18 @@
1
1
  {
2
2
  "name": "@striae-org/striae",
3
- "version": "4.0.0",
3
+ "version": "4.0.2",
4
4
  "private": false,
5
5
  "description": "Striae is a specialized, cloud-native platform designed to streamline forensic firearms identification by providing an intuitive environment for digital comparison image annotation, authenticated confirmations, and automated report generation.",
6
6
  "license": "Apache-2.0",
7
- "homepage": "https://www.striae.org",
7
+ "homepage": "https://github.com/striae-org/striae/wiki",
8
8
  "repository": {
9
9
  "type": "git",
10
10
  "url": "https://github.com/striae-org/striae.git"
11
11
  },
12
- "funding": [
13
- {
14
- "type": "patreon",
15
- "url": "https://www.patreon.com/striae"
16
- }
17
- ],
12
+ "funding": {
13
+ "type": "github",
14
+ "url": "https://github.com/sponsors/striae-org"
15
+ },
18
16
  "bugs": {
19
17
  "url": "https://github.com/striae-org/striae/issues"
20
18
  },
@@ -114,7 +112,7 @@
114
112
  "@react-router/cloudflare": "^7.13.1",
115
113
  "exceljs": "^4.4.0",
116
114
  "firebase": "^12.10.0",
117
- "isbot": "^5.1.35",
115
+ "isbot": "^5.1.36",
118
116
  "jszip": "^3.10.1",
119
117
  "react": "^19.2.4",
120
118
  "react-dom": "^19.2.4",
@@ -123,23 +121,23 @@
123
121
  "devDependencies": {
124
122
  "@react-router/dev": "^7.13.1",
125
123
  "@react-router/fs-routes": "^7.13.1",
126
- "@types/react": "^19.1.10",
127
- "@types/react-dom": "^19.1.7",
128
- "@typescript-eslint/eslint-plugin": "^8.54.0",
129
- "@typescript-eslint/parser": "^8.56.1",
124
+ "@types/react": "^19.2.14",
125
+ "@types/react-dom": "^19.2.3",
126
+ "@typescript-eslint/eslint-plugin": "^8.57.1",
127
+ "@typescript-eslint/parser": "^8.57.1",
130
128
  "autoprefixer": "^10.4.27",
131
- "eslint": "^9.39.2",
129
+ "eslint": "^9.39.4",
132
130
  "eslint-import-resolver-typescript": "^4.4.4",
133
131
  "eslint-plugin-import": "^2.32.0",
134
- "eslint-plugin-jsx-a11y": "^6.7.1",
135
- "eslint-plugin-react": "^7.37.4",
132
+ "eslint-plugin-jsx-a11y": "^6.10.2",
133
+ "eslint-plugin-react": "^7.37.5",
136
134
  "eslint-plugin-react-hooks": "^7.0.1",
137
- "postcss": "^8.5.6",
138
- "tailwindcss": "^3.4.0",
135
+ "postcss": "^8.5.8",
136
+ "tailwindcss": "^3.4.19",
139
137
  "typescript": "^5.9.3",
140
138
  "vite": "^6.4.1",
141
139
  "vite-tsconfig-paths": "^6.1.1",
142
- "wrangler": "^4.73.0"
140
+ "wrangler": "^4.74.0"
143
141
  },
144
142
  "overrides": {
145
143
  "tar": "7.5.11",
@@ -155,4 +153,4 @@
155
153
  "node": ">=20.0.0"
156
154
  },
157
155
  "packageManager": "npm@11.11.0"
158
- }
156
+ }
@@ -0,0 +1,56 @@
1
+ ==================================================================
2
+ https://keybase.io/stephenjlu
3
+ --------------------------------------------------------------------
4
+
5
+ I hereby claim:
6
+
7
+ * I am an admin of https://striae.app
8
+ * I am stephenjlu (https://keybase.io/stephenjlu) on keybase.
9
+ * I have a public key ASAKWYuLxhqhdePAuDulLzWWUusZk7mQi-1lMyjF8lsSbgo
10
+
11
+ To do so, I am signing this object:
12
+
13
+ {
14
+ "body": {
15
+ "key": {
16
+ "eldest_kid": "01200a598b8bc61aa175e3c0b83ba52f359652eb1993b9908bed653328c5f25b126e0a",
17
+ "host": "keybase.io",
18
+ "kid": "01200a598b8bc61aa175e3c0b83ba52f359652eb1993b9908bed653328c5f25b126e0a",
19
+ "uid": "ef43479353eb3b8be30c76fd0919c219",
20
+ "username": "stephenjlu"
21
+ },
22
+ "merkle_root": {
23
+ "ctime": 1773711528,
24
+ "hash": "9519e4709ef4b2ab7ea27c6633fd91e00989841d63cf53dd15500ccbec43152fdb37efd0cefcae90edcae7d84e2584620026334e98ef2feb36241c6aa38aca11",
25
+ "hash_meta": "a8ed0ac295c8a433c99b4606da6e569399fae2b052a5f8d053a2d735d110b265",
26
+ "seqno": 27471249
27
+ },
28
+ "service": {
29
+ "entropy": "e8nGKp/3/msv+czv8R/pIZOA",
30
+ "hostname": "striae.app",
31
+ "protocol": "https:"
32
+ },
33
+ "type": "web_service_binding",
34
+ "version": 2
35
+ },
36
+ "client": {
37
+ "name": "keybase.io go client",
38
+ "version": "6.6.0"
39
+ },
40
+ "ctime": 1773711539,
41
+ "expire_in": 504576000,
42
+ "prev": "20fa9a69a64b9008e2ba1d18171c4eda04d49fc68c2b419cf5156b78faa8b75b",
43
+ "seqno": 26,
44
+ "tag": "signature"
45
+ }
46
+
47
+ which yields the signature:
48
+
49
+ hKRib2R5hqhkZXRhY2hlZMOpaGFzaF90eXBlCqNrZXnEIwEgClmLi8YaoXXjwLg7pS81llLrGZO5kIvtZTMoxfJbEm4Kp3BheWxvYWTESpcCGsQgIPqaaaZLkAjiuh0YFxxO2gTUn8aMK0Gc9RVrePqot1vEIOlO7j2uG/S+PR0TZqsul0pFPx7wKWbK+YYm6cWb9OQYAgHCo3NpZ8RAUVB1DF+DBdsDrp5BVL0eqUuueayhIrABHB63O9f9e03e8MJpZUAWdv8r7eLRcOoSf5p3I+CtsDgeyklUXy6oCKhzaWdfdHlwZSCkaGFzaIKkdHlwZQildmFsdWXEIH2d3PmZB6Yydgvkp783wyinfEsZBLRlgM2WXItXvkvTo3RhZ80CAqd2ZXJzaW9uAQ==
50
+
51
+ And finally, I am proving ownership of this host by posting or
52
+ appending to this document.
53
+
54
+ View my publicly-auditable identity here: https://keybase.io/stephenjlu
55
+
56
+ ==================================================================
@@ -1,7 +1,6 @@
1
- Contact: mailto:info@striae.org
1
+ Contact: mailto:security@striae.org
2
2
  Contact: https://github.com/striae-org/striae/security/advisories/new
3
3
  Expires: 2035-08-15T00:00:00.000Z
4
- Encryption: https://www.striae.org/.well-known/publickey.info@striae.org.asc
5
4
  Preferred-Languages: en
6
- Canonical: https://www.striae.org/.well-known/security.txt
7
- Policy: https://www.striae.org/security
5
+ Canonical: https://striae.app/.well-known/security.txt
6
+ Policy: https://striae.org/security