@striae-org/striae 3.1.0 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/components/icon/icons.svg +4 -71
- package/app/components/icon/manifest.json +3 -75
- package/app/components/public-signing-key-modal/public-signing-key-modal.module.css +171 -0
- package/app/components/public-signing-key-modal/public-signing-key-modal.tsx +223 -0
- package/app/components/sidebar/case-export/case-export.module.css +0 -158
- package/app/components/sidebar/case-export/case-export.tsx +8 -185
- package/app/services/audit-export.service.ts +1 -1
- package/package.json +5 -2
- package/scripts/deploy-pages.sh +2 -2
- package/workers/audit-worker/wrangler.jsonc.example +1 -1
- package/workers/data-worker/wrangler.jsonc.example +1 -1
- package/workers/image-worker/wrangler.jsonc.example +1 -1
- package/workers/keys-worker/wrangler.jsonc.example +1 -1
- package/workers/pdf-worker/wrangler.jsonc.example +1 -1
- package/workers/user-worker/wrangler.jsonc.example +1 -1
- package/wrangler.toml.example +1 -1
|
@@ -1,69 +1,5 @@
|
|
|
1
1
|
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
|
|
2
2
|
<defs>
|
|
3
|
-
<symbol id="send" width="24" height="24" viewBox="0 0 24 24">
|
|
4
|
-
<path d="M2.01 21 23 12 2.01 3 2 10l15 2-15 2 .01 7Z"/>
|
|
5
|
-
</symbol>
|
|
6
|
-
<symbol id="arrow-right" width="24" height="24" viewBox="0 0 24 24">
|
|
7
|
-
<path d="m14.2 4.8 6.5 6.5.71.7-.7.7-6.5 6.5-1.42-1.4 4.8-4.8H3v-2h14.59l-4.8-4.8 1.42-1.4Z"/>
|
|
8
|
-
</symbol>
|
|
9
|
-
<symbol id="chevron-right" width="24" height="24" viewBox="0 0 24 24">
|
|
10
|
-
<path d="m13.59 12-5.8-5.8 1.42-1.4 6.5 6.5.7.7-.7.7-6.5 6.5-1.42-1.4 5.8-5.8Z"/>
|
|
11
|
-
</symbol>
|
|
12
|
-
<symbol id="github" width="24" height="24" viewBox="0 0 24 24">
|
|
13
|
-
<path d="M12 2a10 10 0 0 0-3.16 19.5c.5.08.66-.23.66-.5v-1.69c-2.77.6-3.36-1.34-3.36-1.34-.46-1.16-1.11-1.47-1.11-1.47-.91-.62.07-.6.07-.6 1 .07 1.53 1.03 1.53 1.03.87 1.52 2.34 1.07 2.91.83.09-.65.35-1.09.63-1.34-2.22-.25-4.55-1.11-4.55-4.92 0-1.11.38-2 1.03-2.71-.1-.25-.45-1.29.1-2.64 0 0 .84-.27 2.75 1.02a9.42 9.42 0 0 1 5 0c1.91-1.29 2.75-1.02 2.75-1.02.55 1.35.2 2.39.1 2.64.65.71 1.03 1.6 1.03 2.71 0 3.82-2.34 4.66-4.57 4.91.36.31.69.92.69 1.85V21c0 .27.16.59.67.5a10.03 10.03 0 0 0 3.9-16.57A10 10 0 0 0 12 2Z"/>
|
|
14
|
-
</symbol>
|
|
15
|
-
<symbol id="google" width="24" height="24" viewBox="0 0 24 24">
|
|
16
|
-
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="var(--primary)"/><path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="var(--primary)"/><path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="var(--primary)"/><path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="var(--primary)"/><path d="M1 1h22v22H1z" fill="none"/>
|
|
17
|
-
</symbol>
|
|
18
|
-
<symbol id="menu" width="24" height="24" viewBox="0 0 24 24">
|
|
19
|
-
<path d="M22 6H2V4h20v2ZM2 13h16v-2H2v2Zm0 7h20v-2H2v2Z"/>
|
|
20
|
-
</symbol>
|
|
21
|
-
<symbol id="figma" width="24" height="24" viewBox="0 0 24 24">
|
|
22
|
-
<path fill-rule="evenodd" d="M15 10a2 2 0 1 0 0 4 2 2 0 0 0 0-4Zm-2 5.46A4 4 0 0 0 17.65 9a4.01 4.01 0 0 0 .18-5.83A4 4 0 0 0 15 2H9a4 4 0 0 0-2.65 7 4.01 4.01 0 0 0 0 6A3.98 3.98 0 0 0 5 18a4 4 0 1 0 8 0v-2.54ZM11 16H9a2 2 0 1 0 2 2v-2ZM9 8h2V4H9a2 2 0 1 0 0 4Zm0 2a2 2 0 1 0 0 4h2v-4H9Zm4-2V4h2a2 2 0 0 1 0 4h-2Z"/>
|
|
23
|
-
</symbol>
|
|
24
|
-
<symbol id="copy" width="24" height="24" viewBox="0 0 24 24">
|
|
25
|
-
<path d="M3 2h12v4H7v10H3V2Zm6 20V8h12v11l-3 3H9Z"/>
|
|
26
|
-
</symbol>
|
|
27
|
-
<symbol id="error" width="24" height="24" viewBox="0 0 24 24">
|
|
28
|
-
<path d="M19.7 19H4.3L12 5.07 19.7 19ZM11.12 2.52l-9.39 17L2.61 21h18.78l.88-1.48-9.4-17h-1.75ZM11 14v-4h2v4h-2Zm0 3v-2h2v2h-2Z"/>
|
|
29
|
-
</symbol>
|
|
30
|
-
<symbol id="arrow-left" width="24" height="24" viewBox="0 0 24 24">
|
|
31
|
-
<path d="m9.8 19.2-6.5-6.5-.71-.7.7-.7 6.5-6.5 1.42 1.4L6.4 11H21v2H6.41l4.8 4.8-1.42 1.4Z"/>
|
|
32
|
-
</symbol>
|
|
33
|
-
<symbol id="check" width="24" height="24" viewBox="0 0 24 24">
|
|
34
|
-
<path d="m20.2 7.2-10 10-.7.71-.7-.7-5-5 1.4-1.42 4.3 4.3 9.3-9.3 1.4 1.42Z"/>
|
|
35
|
-
</symbol>
|
|
36
|
-
<symbol id="link" width="24" height="24" viewBox="0 0 24 24">
|
|
37
|
-
<path d="M9 4 8 5v2h2V6h10v7H10v-2H8v3l1 1h12l1-1V5l-1-1H9Zm2 4H3L2 9v9l1 1h12l1-1v-2h-2v1H4v-7h10v2h2V9l-1-1h-4Z"/>
|
|
38
|
-
</symbol>
|
|
39
|
-
<symbol id="close" width="24" height="24" viewBox="0 0 24 24">
|
|
40
|
-
<path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/>
|
|
41
|
-
</symbol>
|
|
42
|
-
<symbol id="pause" width="24" height="24" viewBox="0 0 24 24">
|
|
43
|
-
<path d="M6 19h4V5H6v14Zm8-14v14h4V5h-4Z"/>
|
|
44
|
-
</symbol>
|
|
45
|
-
<symbol id="play" width="24" height="24" viewBox="0 0 24 24">
|
|
46
|
-
<path d="M8 5v14l11-7L8 5Z"/>
|
|
47
|
-
</symbol>
|
|
48
|
-
<symbol id="bluesky" width="24" height="24" viewBox="0 0 24 24">
|
|
49
|
-
<path d="M6.34 3.78C8.63 5.51 11.09 9.01 12 10.9v4.96c0-.1-.04.02-.13.27-.47 1.4-2.32 6.83-6.54 2.49-2.22-2.29-1.19-4.58 2.86-5.27-2.32.4-4.92-.26-5.63-2.82C2.35 9.8 2 5.25 2 4.64c0-3.07 2.68-2.1 4.34-.86Zm11.32 0C15.37 5.51 12.91 9.01 12 10.9v4.96c0-.1.04.02.13.27.47 1.4 2.32 6.83 6.54 2.49 2.22-2.29 1.19-4.58-2.86-5.27 2.32.4 4.92-.26 5.63-2.82.21-.73.56-5.27.56-5.88 0-3.07-2.68-2.1-4.34-.86Z"/>
|
|
50
|
-
</symbol>
|
|
51
|
-
<symbol id="linkedin" width="24" height="24" viewBox="0 0 24 24">
|
|
52
|
-
<path d="M22.49,0H1.51C.68,0,0,.68,0,1.51v20.97c0,.84.68,1.51,1.51,1.51h20.97c.84,0,1.51-.68,1.51-1.51V1.51c0-.84-.68-1.51-1.51-1.51ZM7.12,20.25h-3.38v-10.5h3.38v10.5ZM5.44,7.4c-1.14,0-2.07-.88-2.07-1.96s.93-1.96,2.07-1.96,2.07.88,2.07,1.96-.93,1.96-2.07,1.96ZM20.21,14.62s-.11,4.75-.02,5.63h-3.38c0-1.17,0-2.35-.01-3.54,0-.96-.02-1.92-.04-2.87-.11-.19-.32-.51-.71-.77-.23-.15-.45-.24-.62-.29-.28-.07-.51-.08-.66-.07-.19.01-.33.05-.4.07-.02,0-.26.07-.5.22-.45.27-.72.71-.81.87v6.38h-3.38v-10.5h3.38v1.12c.48-.37,1.79-2.21,4.5-1.12,1.02.53,1.76,1.25,2.25,2.25.15.31.39,1.41.39,1.41v1.21Z"/>
|
|
53
|
-
</symbol>
|
|
54
|
-
<symbol id="soundcloud" width="24" height="24" viewBox="0 0 24 24">
|
|
55
|
-
<path d="M23.9986 14.7331C23.95 15.6191 23.5689 16.4516 22.9349 17.0567C22.3009 17.6618 21.4631 17.9927 20.5964 17.9803H12.419C12.239 17.9786 12.0669 17.9044 11.9399 17.7738C11.8129 17.6432 11.7413 17.4667 11.7405 17.2824V8.2681C11.7345 8.11342 11.7745 7.96051 11.855 7.8295C11.9355 7.6985 12.0529 7.59552 12.1917 7.53411C12.1917 7.53411 12.9439 7 14.5281 7C15.496 6.99882 16.446 7.26605 17.2774 7.77331C17.9263 8.16418 18.489 8.68867 18.9297 9.31361C19.3704 9.93855 19.6797 10.6503 19.8379 11.4039C20.12 11.3222 20.412 11.2814 20.7052 11.2827C21.145 11.2799 21.5808 11.3678 21.9867 11.5412C22.3926 11.7146 22.7602 11.9699 23.0676 12.2919C23.3749 12.6139 23.6158 12.9961 23.7757 13.4155C23.9357 13.8349 24.0115 14.283 23.9986 14.7331Z"/>
|
|
56
|
-
<path d="M10.7195 8.83825C10.7236 8.80025 10.7198 8.76179 10.7084 8.72538C10.697 8.68898 10.6782 8.65545 10.6533 8.62697C10.6284 8.59849 10.5979 8.57572 10.5638 8.56012C10.5297 8.54453 10.4928 8.53647 10.4554 8.53647C10.4181 8.53647 10.3812 8.54453 10.3471 8.56012C10.313 8.57572 10.2825 8.59849 10.2576 8.62697C10.2327 8.65545 10.2139 8.68898 10.2025 8.72538C10.1911 8.76179 10.1873 8.80025 10.1914 8.83825C9.96735 11.8692 9.79452 14.7102 10.1914 17.7313C10.1986 17.7979 10.2295 17.8594 10.2782 17.9041C10.3269 17.9488 10.39 17.9735 10.4554 17.9735C10.5209 17.9735 10.584 17.9488 10.6327 17.9041C10.6814 17.8594 10.7123 17.7979 10.7195 17.7313C11.1452 14.6839 10.9659 11.8954 10.7195 8.83825Z"/>
|
|
57
|
-
<path d="M9.06799 9.8868C9.06064 9.81689 9.0283 9.75222 8.9772 9.70523C8.9261 9.65824 8.85983 9.63225 8.79114 9.63225C8.72246 9.63225 8.65619 9.65824 8.60509 9.70523C8.55398 9.75222 8.52165 9.81689 8.5143 9.8868C8.21825 12.494 8.21825 15.1274 8.5143 17.7346C8.52436 17.8019 8.55765 17.8634 8.60814 17.9078C8.65864 17.9522 8.72299 17.9766 8.78954 17.9766C8.8561 17.9766 8.92045 17.9522 8.97094 17.9078C9.02144 17.8634 9.05473 17.8019 9.06479 17.7346C9.39552 15.1297 9.39659 12.492 9.06799 9.8868Z"/>
|
|
58
|
-
<path d="M7.40691 9.62139C7.39969 9.55398 7.36843 9.49168 7.31911 9.44642C7.26979 9.40116 7.20589 9.37613 7.13966 9.37613C7.07343 9.37613 7.00953 9.40116 6.96021 9.44642C6.91089 9.49168 6.87963 9.55398 6.87242 9.62139C6.61957 12.387 6.49155 14.969 6.87242 17.728C6.87242 17.8002 6.9004 17.8693 6.95022 17.9203C7.00004 17.9713 7.06761 18 7.13806 18C7.20851 18 7.27608 17.9713 7.3259 17.9203C7.37572 17.8693 7.40371 17.8002 7.40371 17.728C7.79738 14.933 7.67575 12.423 7.40691 9.62139Z"/>
|
|
59
|
-
<path d="M5.75542 10.4111C5.75542 10.3363 5.72642 10.2647 5.6748 10.2118C5.62318 10.159 5.55317 10.1293 5.48017 10.1293C5.40717 10.1293 5.33716 10.159 5.28554 10.2118C5.23392 10.2647 5.20492 10.3363 5.20492 10.4111C4.89926 12.8433 4.89926 15.3056 5.20492 17.7378C5.21219 17.8061 5.24381 17.8692 5.29372 17.915C5.34363 17.9609 5.40832 17.9862 5.47537 17.9862C5.54242 17.9862 5.60711 17.9609 5.65702 17.915C5.70693 17.8692 5.73856 17.8061 5.74582 17.7378C6.07706 15.3076 6.08029 12.8422 5.75542 10.4111Z"/>
|
|
60
|
-
<path d="M4.08793 12.2428C4.08793 12.1689 4.05927 12.0981 4.00825 12.0458C3.95723 11.9936 3.88804 11.9642 3.81588 11.9642C3.74373 11.9642 3.67454 11.9936 3.62352 12.0458C3.5725 12.0981 3.54384 12.1689 3.54384 12.2428C3.15017 14.1302 3.3358 15.8472 3.55984 17.7575C3.56951 17.8199 3.60061 17.8767 3.64754 17.9178C3.69447 17.9588 3.75417 17.9814 3.81588 17.9814C3.8776 17.9814 3.93729 17.9588 3.98423 17.9178C4.03116 17.8767 4.06226 17.8199 4.07193 17.7575C4.31837 15.821 4.5072 14.1433 4.08793 12.2428Z"/>
|
|
61
|
-
<path d="M2.43324 11.9544C2.42584 11.8837 2.39315 11.8182 2.34145 11.7706C2.28975 11.7231 2.2227 11.6968 2.15319 11.6968C2.08369 11.6968 2.01663 11.7231 1.96494 11.7706C1.91324 11.8182 1.88054 11.8837 1.87314 11.9544C1.52428 13.8811 1.6395 15.5785 1.88275 17.5019C1.90835 17.7936 2.39163 17.7903 2.42364 17.5019C2.69249 15.549 2.81731 13.9008 2.43324 11.9544Z"/>
|
|
62
|
-
<path d="M0.762552 12.8981C0.755153 12.8274 0.72246 12.7619 0.670761 12.7143C0.619063 12.6668 0.55201 12.6405 0.482504 12.6405C0.412997 12.6405 0.345944 12.6668 0.294246 12.7143C0.242547 12.7619 0.209854 12.8274 0.202455 12.8981C-0.117601 14.1924 -0.0183835 15.2639 0.224859 16.5615C0.231981 16.6272 0.26253 16.688 0.310657 16.7321C0.358784 16.7762 0.42111 16.8006 0.485704 16.8006C0.550298 16.8006 0.612624 16.7762 0.660751 16.7321C0.708879 16.688 0.739427 16.6272 0.746549 16.5615C1.0282 15.2377 1.16582 14.1892 0.762552 12.8981Z"/>
|
|
63
|
-
</symbol>
|
|
64
|
-
<symbol id="tictactoe" width="24" height="24" viewBox="0 0 24 24">
|
|
65
|
-
<path d="M23.94,15.68c-.11-.32-1.37-.2-1.85-.2h-5.85v-6.98h7.45c.1-.03.17-.11.24-.16.03-.14.05-.2.04-.36-.05-.05-.12-.14-.16-.2-.26-.06-.65-.04-.98-.04h-6.6V.76c0-.18.03-.45-.04-.56-.06-.19-.26-.21-.47-.2-.05.05-.12.1-.18.13-.17.66-.07,1.77-.07,2.56v5.05h-6.98V1.83c0-.4.09-1.46-.07-1.69C8.35,0,8.18,0,8,0c-.08.08-.21.13-.24.24v7.49H.77c-.18,0-.45-.03-.56.04-.17.05-.29.35-.16.51.11.32,1.37.2,1.85.2h5.85v6.98H.3c-.1.03-.17.11-.24.16-.03.14-.05.2-.04.36.05.05.12.14.16.2.26.06.65.04.98.04h6.6v6.98c0,.18-.03.45.04.56.06.19.26.21.47.2.05-.05.12-.1.18-.13.17-.66.07-1.77.07-2.56v-5.05h6.98v5.92c0,.4-.09,1.46.07,1.69.09.14.26.14.44.13.08-.08.21-.13.24-.24v-7.49h6.98c.18,0,.45.03.56-.04.17-.05.29-.35.16-.51ZM15.49,15.48h-6.98v-6.98h6.98v6.98Z"/>
|
|
66
|
-
</symbol>
|
|
67
3
|
<symbol id="number" viewBox="0 0 24 24">
|
|
68
4
|
<path d="M1,9h24M1,17h24M10,1l-3,24M19,1l-3,24" style="stroke:#1e1e1e; stroke-linecap:round; stroke-linejoin:round; stroke-width:2px;" />
|
|
69
5
|
</symbol>
|
|
@@ -85,6 +21,9 @@
|
|
|
85
21
|
<polygon points="18.3 .8 5.7 .8 12 7.93 18.3 .8" style="fill-rule:evenodd;"/>
|
|
86
22
|
<polyline points="0 23.75 0 0 24 0 24 23.75" style="fill:none; fill-rule:evenodd;"/>
|
|
87
23
|
</symbol>
|
|
24
|
+
<symbol id="box" viewBox="0 0 24 24">
|
|
25
|
+
<path d="M2.67,24c-.73,0-1.36-.26-1.88-.78-.52-.52-.78-1.15-.78-1.88V2.67c0-.73.26-1.36.78-1.88s1.15-.78,1.88-.78h18.67c.73,0,1.36.26,1.88.78.52.52.78,1.15.78,1.88v18.67c0,.73-.26,1.36-.78,1.88-.52.52-1.15.78-1.88.78H2.67ZM2.67,21.33h18.67V2.67H2.67v18.67Z"/>
|
|
26
|
+
</symbol>
|
|
88
27
|
<symbol id="eye" viewBox="0 0 24 24">
|
|
89
28
|
<path d="M1 12C1 12 5 4 12 4C19 4 23 12 23 12C23 12 19 20 12 20C5 20 1 12 1 12Z" stroke="#1E1E1E" stroke-width="2.5" stroke-linecap="round" fill="none" stroke-linejoin="round"/>
|
|
90
29
|
<path d="M12 15C13.6569 15 15 13.6569 15 12C15 10.3431 13.6569 9 12 9C10.3431 9 9 10.3431 9 12C9 13.6569 10.3431 15 12 15Z" stroke="#1E1E1E" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
@@ -92,11 +31,5 @@
|
|
|
92
31
|
<symbol id="eye-off" viewBox="0 0 24 24">
|
|
93
32
|
<path d="M17.94 17.94C16.2306 19.243 14.1491 19.9649 12 20C5 20 1 12 1 12C2.24389 9.6819 3.96914 7.65661 6.06 6.06M9.9 4.24C10.5883 4.07888 11.2931 3.99834 12 4C19 4 23 12 23 12C22.393 13.1356 21.6691 14.2047 20.84 15.19M14.12 14.12C13.8454 14.4147 13.5141 14.6512 13.1462 14.8151C12.7782 14.9791 12.3809 15.0673 11.9781 15.0744C11.5753 15.0815 11.1752 15.0074 10.8016 14.8565C10.4281 14.7056 10.0887 14.481 9.80385 14.1962C9.51897 13.9113 9.29439 13.5719 9.14351 13.1984C8.99262 12.8248 8.91853 12.4247 8.92563 12.0219C8.93274 11.6191 9.02091 11.2218 9.18488 10.8538C9.34884 10.4859 9.58525 10.1546 9.88 9.88M1 1L23 23" stroke="#1E1E1E" fill="none" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"/>
|
|
94
33
|
</symbol>
|
|
95
|
-
<symbol id="globe" viewBox="0 0 24 24">
|
|
96
|
-
<path d="M12,24c-1.64,0-3.19-.32-4.65-.95s-2.74-1.49-3.82-2.58-1.95-2.36-2.58-3.83c-.63-1.46-.95-3.01-.95-4.65s.31-3.22.95-4.66c.63-1.45,1.49-2.72,2.58-3.81s2.36-1.95,3.82-2.58c1.46-.63,3.01-.95,4.65-.95s3.22.31,4.66.95c1.45.63,2.72,1.49,3.81,2.58s1.95,2.36,2.58,3.81c.63,1.45.95,3.01.95,4.66s-.32,3.19-.95,4.65-1.49,2.74-2.58,3.83-2.36,1.95-3.81,2.58c-1.45.63-3.01.95-4.66.95ZM12,21.54c.52-.72.97-1.47,1.35-2.25.38-.78.69-1.61.93-2.49h-4.56c.24.88.55,1.71.93,2.49.38.78.83,1.53,1.35,2.25ZM8.88,21.06c-.36-.66-.68-1.34-.94-2.05-.27-.71-.5-1.45-.67-2.2h-3.54c.58,1,1.31,1.87,2.18,2.61.87.74,1.87,1.29,2.99,1.65ZM15.12,21.06c1.12-.36,2.11-.91,2.98-1.65.87-.74,1.6-1.61,2.18-2.61h-3.54c-.18.76-.4,1.49-.67,2.2-.27.71-.59,1.4-.94,2.05ZM2.7,14.4h4.08c-.06-.4-.11-.8-.14-1.19s-.04-.79-.04-1.21.01-.82.04-1.21.07-.79.14-1.19H2.7c-.1.4-.17.8-.23,1.19s-.07.79-.07,1.21.02.82.07,1.21.13.79.23,1.19ZM9.18,14.4h5.64c.06-.4.1-.8.13-1.19.03-.39.05-.79.05-1.21s-.01-.82-.05-1.21c-.03-.39-.08-.79-.13-1.19h-5.64c-.06.4-.11.8-.14,1.19s-.04.79-.04,1.21.01.82.04,1.21.07.79.14,1.19ZM17.22,14.4h4.08c.1-.4.17-.8.22-1.19s.08-.79.08-1.21-.02-.82-.08-1.21-.13-.79-.22-1.19h-4.08c.06.4.1.8.13,1.19.03.39.05.79.05,1.21s-.01.82-.05,1.21c-.03.39-.08.79-.13,1.19ZM16.74,7.2h3.54c-.58-1-1.3-1.87-2.18-2.61-.87-.74-1.87-1.29-2.98-1.65.36.66.67,1.34.94,2.05.27.71.5,1.45.67,2.21ZM9.72,7.2h4.56c-.24-.88-.55-1.71-.93-2.49s-.83-1.53-1.35-2.25c-.52.72-.97,1.47-1.35,2.25s-.69,1.61-.93,2.49ZM3.72,7.2h3.54c.18-.76.4-1.49.67-2.21.27-.71.58-1.39.94-2.05-1.12.36-2.12.91-2.99,1.65-.87.74-1.6,1.61-2.18,2.61Z"/>
|
|
97
|
-
</symbol>
|
|
98
|
-
<symbol id="box" viewBox="0 0 24 24">
|
|
99
|
-
<path d="M2.67,24c-.73,0-1.36-.26-1.88-.78-.52-.52-.78-1.15-.78-1.88V2.67c0-.73.26-1.36.78-1.88s1.15-.78,1.88-.78h18.67c.73,0,1.36.26,1.88.78.52.52.78,1.15.78,1.88v18.67c0,.73-.26,1.36-.78,1.88-.52.52-1.15.78-1.88.78H2.67ZM2.67,21.33h18.67V2.67H2.67v18.67Z"/>
|
|
100
|
-
</symbol>
|
|
101
34
|
</defs>
|
|
102
|
-
</svg>
|
|
35
|
+
</svg>
|
|
@@ -1,76 +1,4 @@
|
|
|
1
1
|
{
|
|
2
|
-
"send": {
|
|
3
|
-
"width": 24,
|
|
4
|
-
"height": 24
|
|
5
|
-
},
|
|
6
|
-
"arrow-right": {
|
|
7
|
-
"width": 24,
|
|
8
|
-
"height": 24
|
|
9
|
-
},
|
|
10
|
-
"chevron-right": {
|
|
11
|
-
"width": 24,
|
|
12
|
-
"height": 24
|
|
13
|
-
},
|
|
14
|
-
"github": {
|
|
15
|
-
"width": 24,
|
|
16
|
-
"height": 24
|
|
17
|
-
},
|
|
18
|
-
"google": {
|
|
19
|
-
"width": 24,
|
|
20
|
-
"height": 24
|
|
21
|
-
},
|
|
22
|
-
"menu": {
|
|
23
|
-
"width": 24,
|
|
24
|
-
"height": 24
|
|
25
|
-
},
|
|
26
|
-
"figma": {
|
|
27
|
-
"width": 24,
|
|
28
|
-
"height": 24
|
|
29
|
-
},
|
|
30
|
-
"copy": {
|
|
31
|
-
"width": 24,
|
|
32
|
-
"height": 24
|
|
33
|
-
},
|
|
34
|
-
"error": {
|
|
35
|
-
"width": 24,
|
|
36
|
-
"height": 24
|
|
37
|
-
},
|
|
38
|
-
"arrow-left": {
|
|
39
|
-
"width": 24,
|
|
40
|
-
"height": 24
|
|
41
|
-
},
|
|
42
|
-
"check": {
|
|
43
|
-
"width": 24,
|
|
44
|
-
"height": 24
|
|
45
|
-
},
|
|
46
|
-
"link": {
|
|
47
|
-
"width": 24,
|
|
48
|
-
"height": 24
|
|
49
|
-
},
|
|
50
|
-
"close": {
|
|
51
|
-
"width": 24,
|
|
52
|
-
"height": 24
|
|
53
|
-
},
|
|
54
|
-
"pause": {
|
|
55
|
-
"width": 24,
|
|
56
|
-
"height": 24
|
|
57
|
-
},
|
|
58
|
-
"play": {
|
|
59
|
-
"width": 24,
|
|
60
|
-
"height": 24
|
|
61
|
-
},
|
|
62
|
-
"bluesky": {
|
|
63
|
-
"width": 24,
|
|
64
|
-
"height": 24
|
|
65
|
-
},
|
|
66
|
-
"linkedin": {
|
|
67
|
-
"width": 24,
|
|
68
|
-
"height": 24
|
|
69
|
-
},
|
|
70
|
-
"soundcloud": {
|
|
71
|
-
"width": 24,
|
|
72
|
-
"height": 24
|
|
73
|
-
},
|
|
74
2
|
"number": {
|
|
75
3
|
"width": 24,
|
|
76
4
|
"height": 24
|
|
@@ -84,8 +12,8 @@
|
|
|
84
12
|
"height": 24
|
|
85
13
|
},
|
|
86
14
|
"print": {
|
|
87
|
-
"width":
|
|
88
|
-
"height":
|
|
15
|
+
"width": 28,
|
|
16
|
+
"height": 28
|
|
89
17
|
},
|
|
90
18
|
"id": {
|
|
91
19
|
"width": 24,
|
|
@@ -95,7 +23,7 @@
|
|
|
95
23
|
"width": 24,
|
|
96
24
|
"height": 24
|
|
97
25
|
},
|
|
98
|
-
"
|
|
26
|
+
"box": {
|
|
99
27
|
"width": 24,
|
|
100
28
|
"height": 24
|
|
101
29
|
},
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
.overlay {
|
|
2
|
+
position: fixed;
|
|
3
|
+
inset: 0;
|
|
4
|
+
background-color: color-mix(in lab, var(--background) 60%, transparent);
|
|
5
|
+
display: flex;
|
|
6
|
+
justify-content: center;
|
|
7
|
+
align-items: center;
|
|
8
|
+
z-index: var(--zIndex5);
|
|
9
|
+
padding: var(--spaceL);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.modal {
|
|
13
|
+
width: 100%;
|
|
14
|
+
max-width: 640px;
|
|
15
|
+
max-height: 90vh;
|
|
16
|
+
background: var(--backgroundLight);
|
|
17
|
+
border-radius: var(--spaceXS);
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
box-shadow: 0 var(--spaceXS) var(--spaceL)
|
|
21
|
+
color-mix(in lab, var(--black) 18%, transparent);
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
cursor: default;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.header {
|
|
27
|
+
display: flex;
|
|
28
|
+
justify-content: space-between;
|
|
29
|
+
align-items: center;
|
|
30
|
+
padding: var(--spaceL);
|
|
31
|
+
border-bottom: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.title {
|
|
35
|
+
margin: 0;
|
|
36
|
+
font-size: var(--fontSizeBodyL);
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
color: var(--textTitle);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.closeButton {
|
|
42
|
+
background: none;
|
|
43
|
+
border: none;
|
|
44
|
+
font-size: var(--fontSizeH5);
|
|
45
|
+
cursor: pointer;
|
|
46
|
+
padding: var(--spaceS);
|
|
47
|
+
color: var(--textLight);
|
|
48
|
+
transition: color var(--durationS) var(--bezierFastoutSlowin);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.closeButton:hover {
|
|
52
|
+
color: var(--text);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.content {
|
|
56
|
+
padding: var(--spaceL);
|
|
57
|
+
flex: 1 1 auto;
|
|
58
|
+
min-height: 0;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
gap: var(--spaceM);
|
|
62
|
+
overflow-y: auto;
|
|
63
|
+
overflow-x: hidden;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.description {
|
|
67
|
+
margin: 0;
|
|
68
|
+
font-size: var(--fontSizeBodyS);
|
|
69
|
+
color: var(--textBody);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
.meta {
|
|
73
|
+
margin: 0;
|
|
74
|
+
font-size: var(--fontSizeBodyS);
|
|
75
|
+
color: var(--textTitle);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.meta span {
|
|
79
|
+
font-weight: var(--fontWeightMedium);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.label {
|
|
83
|
+
font-size: var(--fontSizeBodyXS);
|
|
84
|
+
font-weight: var(--fontWeightMedium);
|
|
85
|
+
color: var(--textTitle);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.field {
|
|
89
|
+
width: 100%;
|
|
90
|
+
max-width: 100%;
|
|
91
|
+
box-sizing: border-box;
|
|
92
|
+
min-height: 180px;
|
|
93
|
+
padding: var(--spaceM);
|
|
94
|
+
border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
95
|
+
border-radius: var(--spaceXS);
|
|
96
|
+
background: color-mix(in lab, var(--background) 96%, transparent);
|
|
97
|
+
color: var(--textBody);
|
|
98
|
+
font-size: var(--fontSizeBodyXS);
|
|
99
|
+
line-height: 1.4;
|
|
100
|
+
font-family: Consolas, "Courier New", monospace;
|
|
101
|
+
resize: vertical;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.howToTitle {
|
|
105
|
+
margin: 0;
|
|
106
|
+
font-size: var(--fontSizeBodyS);
|
|
107
|
+
font-weight: var(--fontWeightMedium);
|
|
108
|
+
color: var(--textTitle);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.howToList {
|
|
112
|
+
margin: 0;
|
|
113
|
+
padding-left: var(--spaceL);
|
|
114
|
+
display: flex;
|
|
115
|
+
flex-direction: column;
|
|
116
|
+
gap: var(--spaceXS);
|
|
117
|
+
color: var(--textBody);
|
|
118
|
+
font-size: var(--fontSizeBodyS);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.actions {
|
|
122
|
+
display: flex;
|
|
123
|
+
justify-content: flex-end;
|
|
124
|
+
gap: var(--spaceS);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
.status {
|
|
128
|
+
margin: 0;
|
|
129
|
+
font-size: var(--fontSizeBodyXS);
|
|
130
|
+
color: var(--textBody);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.copyButton {
|
|
134
|
+
background: transparent;
|
|
135
|
+
color: var(--primary);
|
|
136
|
+
border: 1px solid color-mix(in lab, var(--primary) 35%, transparent);
|
|
137
|
+
border-radius: var(--spaceXS);
|
|
138
|
+
padding: var(--spaceS) var(--spaceL);
|
|
139
|
+
font-size: var(--fontSizeBodyS);
|
|
140
|
+
font-weight: var(--fontWeightMedium);
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
.copyButton:hover:not(:disabled) {
|
|
146
|
+
background: color-mix(in lab, var(--primary) 10%, transparent);
|
|
147
|
+
border-color: color-mix(in lab, var(--primary) 55%, transparent);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.copyButton:disabled {
|
|
151
|
+
background: color-mix(in lab, var(--background) 95%, transparent);
|
|
152
|
+
color: var(--textLight);
|
|
153
|
+
border-color: color-mix(in lab, var(--text) 10%, transparent);
|
|
154
|
+
cursor: not-allowed;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.closeModalButton {
|
|
158
|
+
background: var(--primary);
|
|
159
|
+
color: white;
|
|
160
|
+
border: none;
|
|
161
|
+
border-radius: var(--spaceXS);
|
|
162
|
+
padding: var(--spaceS) var(--spaceL);
|
|
163
|
+
font-size: var(--fontSizeBodyS);
|
|
164
|
+
font-weight: var(--fontWeightMedium);
|
|
165
|
+
cursor: pointer;
|
|
166
|
+
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.closeModalButton:hover {
|
|
170
|
+
background: color-mix(in lab, var(--primary) 85%, var(--black));
|
|
171
|
+
}
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { useEffect, useId, useState, type KeyboardEvent, type MouseEvent } from 'react';
|
|
2
|
+
import styles from './public-signing-key-modal.module.css';
|
|
3
|
+
|
|
4
|
+
const NO_PUBLIC_KEY_MESSAGE = 'No public signing key is configured for this environment.';
|
|
5
|
+
|
|
6
|
+
interface PublicSigningKeyModalProps {
|
|
7
|
+
isOpen: boolean;
|
|
8
|
+
onClose: () => void;
|
|
9
|
+
publicSigningKeyId?: string | null;
|
|
10
|
+
publicKeyPem?: string | null;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const PublicSigningKeyModal = ({
|
|
14
|
+
isOpen,
|
|
15
|
+
onClose,
|
|
16
|
+
publicSigningKeyId,
|
|
17
|
+
publicKeyPem
|
|
18
|
+
}: PublicSigningKeyModalProps) => {
|
|
19
|
+
const [isCopyingPublicKey, setIsCopyingPublicKey] = useState(false);
|
|
20
|
+
const [publicKeyCopyMessage, setPublicKeyCopyMessage] = useState('');
|
|
21
|
+
const publicSigningKeyTitleId = useId();
|
|
22
|
+
const publicSigningKeyFieldId = useId();
|
|
23
|
+
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
if (!isOpen) {
|
|
26
|
+
setIsCopyingPublicKey(false);
|
|
27
|
+
setPublicKeyCopyMessage('');
|
|
28
|
+
}
|
|
29
|
+
}, [isOpen]);
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!isOpen) {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const handleEscapeKey = (event: globalThis.KeyboardEvent) => {
|
|
37
|
+
if (event.key === 'Escape') {
|
|
38
|
+
onClose();
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
document.addEventListener('keydown', handleEscapeKey);
|
|
43
|
+
|
|
44
|
+
return () => {
|
|
45
|
+
document.removeEventListener('keydown', handleEscapeKey);
|
|
46
|
+
};
|
|
47
|
+
}, [isOpen, onClose]);
|
|
48
|
+
|
|
49
|
+
if (!isOpen) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const handleOverlayMouseDown = (event: MouseEvent<HTMLDivElement>) => {
|
|
54
|
+
if (event.target === event.currentTarget) {
|
|
55
|
+
onClose();
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleOverlayKeyDown = (event: KeyboardEvent<HTMLDivElement>) => {
|
|
60
|
+
if (event.target !== event.currentTarget) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (event.key === 'Enter' || event.key === ' ') {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
onClose();
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
const copyTextWithExecCommand = (text: string): boolean => {
|
|
71
|
+
const tempTextarea = document.createElement('textarea');
|
|
72
|
+
tempTextarea.value = text;
|
|
73
|
+
tempTextarea.setAttribute('readonly', '');
|
|
74
|
+
tempTextarea.style.position = 'fixed';
|
|
75
|
+
tempTextarea.style.opacity = '0';
|
|
76
|
+
tempTextarea.style.pointerEvents = 'none';
|
|
77
|
+
|
|
78
|
+
document.body.appendChild(tempTextarea);
|
|
79
|
+
tempTextarea.select();
|
|
80
|
+
|
|
81
|
+
let copied = false;
|
|
82
|
+
try {
|
|
83
|
+
copied = document.execCommand('copy');
|
|
84
|
+
} finally {
|
|
85
|
+
document.body.removeChild(tempTextarea);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return copied;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleCopyPublicKey = async () => {
|
|
92
|
+
if (!publicKeyPem) {
|
|
93
|
+
setPublicKeyCopyMessage(NO_PUBLIC_KEY_MESSAGE);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setIsCopyingPublicKey(true);
|
|
98
|
+
setPublicKeyCopyMessage('');
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
102
|
+
await navigator.clipboard.writeText(publicKeyPem);
|
|
103
|
+
setPublicKeyCopyMessage('Public key copied to clipboard.');
|
|
104
|
+
} else {
|
|
105
|
+
const copied = copyTextWithExecCommand(publicKeyPem);
|
|
106
|
+
setPublicKeyCopyMessage(
|
|
107
|
+
copied
|
|
108
|
+
? 'Public key copied to clipboard.'
|
|
109
|
+
: 'Copy failed. Select and copy the key manually.'
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
} catch (copyError) {
|
|
113
|
+
const copied = copyTextWithExecCommand(publicKeyPem);
|
|
114
|
+
setPublicKeyCopyMessage(
|
|
115
|
+
copied
|
|
116
|
+
? 'Public key copied to clipboard.'
|
|
117
|
+
: 'Copy failed. Select and copy the key manually.'
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
if (!copied) {
|
|
121
|
+
console.error('Failed to copy public signing key:', copyError);
|
|
122
|
+
}
|
|
123
|
+
} finally {
|
|
124
|
+
setIsCopyingPublicKey(false);
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<div
|
|
130
|
+
className={styles.overlay}
|
|
131
|
+
onMouseDown={handleOverlayMouseDown}
|
|
132
|
+
onKeyDown={handleOverlayKeyDown}
|
|
133
|
+
role="button"
|
|
134
|
+
tabIndex={0}
|
|
135
|
+
aria-label="Close public signing key dialog"
|
|
136
|
+
>
|
|
137
|
+
<div
|
|
138
|
+
className={styles.modal}
|
|
139
|
+
role="dialog"
|
|
140
|
+
aria-modal="true"
|
|
141
|
+
aria-labelledby={publicSigningKeyTitleId}
|
|
142
|
+
>
|
|
143
|
+
<div className={styles.header}>
|
|
144
|
+
<h3 id={publicSigningKeyTitleId} className={styles.title}>
|
|
145
|
+
Striae Public Signing Key
|
|
146
|
+
</h3>
|
|
147
|
+
<button
|
|
148
|
+
type="button"
|
|
149
|
+
className={styles.closeButton}
|
|
150
|
+
onClick={onClose}
|
|
151
|
+
aria-label="Close public signing key dialog"
|
|
152
|
+
>
|
|
153
|
+
×
|
|
154
|
+
</button>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div className={styles.content}>
|
|
158
|
+
<p className={styles.description}>
|
|
159
|
+
This key verifies digital signatures attached to Striae exports. It is safe to share for
|
|
160
|
+
independent verification.
|
|
161
|
+
</p>
|
|
162
|
+
|
|
163
|
+
{publicSigningKeyId && (
|
|
164
|
+
<p className={styles.meta}>
|
|
165
|
+
Key ID: <span>{publicSigningKeyId}</span>
|
|
166
|
+
</p>
|
|
167
|
+
)}
|
|
168
|
+
|
|
169
|
+
<label htmlFor={publicSigningKeyFieldId} className={styles.label}>
|
|
170
|
+
Public signing key (PEM)
|
|
171
|
+
</label>
|
|
172
|
+
<textarea
|
|
173
|
+
id={publicSigningKeyFieldId}
|
|
174
|
+
className={styles.field}
|
|
175
|
+
value={publicKeyPem || NO_PUBLIC_KEY_MESSAGE}
|
|
176
|
+
readOnly
|
|
177
|
+
rows={10}
|
|
178
|
+
/>
|
|
179
|
+
|
|
180
|
+
<p className={styles.howToTitle}>How to verify Striae exports</p>
|
|
181
|
+
<ol className={styles.howToList}>
|
|
182
|
+
<li>
|
|
183
|
+
Locate signature metadata in the export (for case ZIP exports, see FORENSIC_MANIFEST.json;
|
|
184
|
+
for confirmation exports, see metadata.signature).
|
|
185
|
+
</li>
|
|
186
|
+
<li>
|
|
187
|
+
Use this public key with your signature verification workflow (for example OpenSSL or an
|
|
188
|
+
internal verifier) to validate the signed payload.
|
|
189
|
+
</li>
|
|
190
|
+
<li>
|
|
191
|
+
Trust the export only when signature verification succeeds and the key ID matches the export
|
|
192
|
+
metadata.
|
|
193
|
+
</li>
|
|
194
|
+
</ol>
|
|
195
|
+
|
|
196
|
+
{publicKeyCopyMessage && (
|
|
197
|
+
<p className={styles.status} role="status" aria-live="polite">
|
|
198
|
+
{publicKeyCopyMessage}
|
|
199
|
+
</p>
|
|
200
|
+
)}
|
|
201
|
+
|
|
202
|
+
<div className={styles.actions}>
|
|
203
|
+
<button
|
|
204
|
+
type="button"
|
|
205
|
+
className={styles.copyButton}
|
|
206
|
+
onClick={handleCopyPublicKey}
|
|
207
|
+
disabled={isCopyingPublicKey || !publicKeyPem}
|
|
208
|
+
>
|
|
209
|
+
{isCopyingPublicKey ? 'Copying...' : 'Copy Key'}
|
|
210
|
+
</button>
|
|
211
|
+
<button
|
|
212
|
+
type="button"
|
|
213
|
+
className={styles.closeModalButton}
|
|
214
|
+
onClick={onClose}
|
|
215
|
+
>
|
|
216
|
+
Close
|
|
217
|
+
</button>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
</div>
|
|
222
|
+
);
|
|
223
|
+
};
|
|
@@ -317,164 +317,6 @@
|
|
|
317
317
|
border-color: color-mix(in lab, var(--primary) 55%, transparent);
|
|
318
318
|
}
|
|
319
319
|
|
|
320
|
-
.publicKeyOverlay {
|
|
321
|
-
position: fixed;
|
|
322
|
-
inset: 0;
|
|
323
|
-
background-color: color-mix(in lab, var(--background) 60%, transparent);
|
|
324
|
-
display: flex;
|
|
325
|
-
justify-content: center;
|
|
326
|
-
align-items: center;
|
|
327
|
-
z-index: var(--zIndex5);
|
|
328
|
-
padding: var(--spaceL);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
.publicKeyModal {
|
|
332
|
-
width: 100%;
|
|
333
|
-
max-width: 640px;
|
|
334
|
-
max-height: 90vw;
|
|
335
|
-
background: var(--backgroundLight);
|
|
336
|
-
border-radius: var(--spaceXS);
|
|
337
|
-
display: flex;
|
|
338
|
-
flex-direction: column;
|
|
339
|
-
box-shadow: 0 var(--spaceXS) var(--spaceL)
|
|
340
|
-
color-mix(in lab, var(--black) 18%, transparent);
|
|
341
|
-
overflow: hidden;
|
|
342
|
-
cursor: default;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
.publicKeyHeader {
|
|
346
|
-
display: flex;
|
|
347
|
-
justify-content: space-between;
|
|
348
|
-
align-items: center;
|
|
349
|
-
padding: var(--spaceL);
|
|
350
|
-
border-bottom: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
.publicKeyTitle {
|
|
354
|
-
margin: 0;
|
|
355
|
-
font-size: var(--fontSizeBodyL);
|
|
356
|
-
font-weight: 600;
|
|
357
|
-
color: var(--textTitle);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
.publicKeyContent {
|
|
361
|
-
padding: var(--spaceL);
|
|
362
|
-
flex: 1 1 auto;
|
|
363
|
-
min-height: 0;
|
|
364
|
-
display: flex;
|
|
365
|
-
flex-direction: column;
|
|
366
|
-
gap: var(--spaceM);
|
|
367
|
-
overflow-y: auto;
|
|
368
|
-
overflow-x: hidden;
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
.publicKeyDescription {
|
|
372
|
-
margin: 0;
|
|
373
|
-
font-size: var(--fontSizeBodyS);
|
|
374
|
-
color: var(--textBody);
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
.publicKeyMeta {
|
|
378
|
-
margin: 0;
|
|
379
|
-
font-size: var(--fontSizeBodyS);
|
|
380
|
-
color: var(--textTitle);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
.publicKeyMeta span {
|
|
384
|
-
font-weight: var(--fontWeightMedium);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
.publicKeyLabel {
|
|
388
|
-
font-size: var(--fontSizeBodyXS);
|
|
389
|
-
font-weight: var(--fontWeightMedium);
|
|
390
|
-
color: var(--textTitle);
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
.publicKeyField {
|
|
394
|
-
width: 100%;
|
|
395
|
-
max-width: 100%;
|
|
396
|
-
box-sizing: border-box;
|
|
397
|
-
min-height: 180px;
|
|
398
|
-
padding: var(--spaceM);
|
|
399
|
-
border: 1px solid color-mix(in lab, var(--text) 10%, transparent);
|
|
400
|
-
border-radius: var(--spaceXS);
|
|
401
|
-
background: color-mix(in lab, var(--background) 96%, transparent);
|
|
402
|
-
color: var(--textBody);
|
|
403
|
-
font-size: var(--fontSizeBodyXS);
|
|
404
|
-
line-height: 1.4;
|
|
405
|
-
font-family: Consolas, "Courier New", monospace;
|
|
406
|
-
resize: vertical;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
.publicKeyHowToTitle {
|
|
410
|
-
margin: 0;
|
|
411
|
-
font-size: var(--fontSizeBodyS);
|
|
412
|
-
font-weight: var(--fontWeightMedium);
|
|
413
|
-
color: var(--textTitle);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
.publicKeyHowToList {
|
|
417
|
-
margin: 0;
|
|
418
|
-
padding-left: var(--spaceL);
|
|
419
|
-
display: flex;
|
|
420
|
-
flex-direction: column;
|
|
421
|
-
gap: var(--spaceXS);
|
|
422
|
-
color: var(--textBody);
|
|
423
|
-
font-size: var(--fontSizeBodyS);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
.publicKeyActions {
|
|
427
|
-
display: flex;
|
|
428
|
-
justify-content: flex-end;
|
|
429
|
-
gap: var(--spaceS);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
|
-
.publicKeyStatus {
|
|
433
|
-
margin: 0;
|
|
434
|
-
font-size: var(--fontSizeBodyXS);
|
|
435
|
-
color: var(--textBody);
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
.publicKeyCopyButton {
|
|
439
|
-
background: transparent;
|
|
440
|
-
color: var(--primary);
|
|
441
|
-
border: 1px solid color-mix(in lab, var(--primary) 35%, transparent);
|
|
442
|
-
border-radius: var(--spaceXS);
|
|
443
|
-
padding: var(--spaceS) var(--spaceL);
|
|
444
|
-
font-size: var(--fontSizeBodyS);
|
|
445
|
-
font-weight: var(--fontWeightMedium);
|
|
446
|
-
cursor: pointer;
|
|
447
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
.publicKeyCopyButton:hover:not(:disabled) {
|
|
451
|
-
background: color-mix(in lab, var(--primary) 10%, transparent);
|
|
452
|
-
border-color: color-mix(in lab, var(--primary) 55%, transparent);
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
.publicKeyCopyButton:disabled {
|
|
456
|
-
background: color-mix(in lab, var(--background) 95%, transparent);
|
|
457
|
-
color: var(--textLight);
|
|
458
|
-
border-color: color-mix(in lab, var(--text) 10%, transparent);
|
|
459
|
-
cursor: not-allowed;
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
.publicKeyCloseButton {
|
|
463
|
-
background: var(--primary);
|
|
464
|
-
color: white;
|
|
465
|
-
border: none;
|
|
466
|
-
border-radius: var(--spaceXS);
|
|
467
|
-
padding: var(--spaceS) var(--spaceL);
|
|
468
|
-
font-size: var(--fontSizeBodyS);
|
|
469
|
-
font-weight: var(--fontWeightMedium);
|
|
470
|
-
cursor: pointer;
|
|
471
|
-
transition: all var(--durationS) var(--bezierFastoutSlowin);
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
.publicKeyCloseButton:hover {
|
|
475
|
-
background: color-mix(in lab, var(--primary) 85%, var(--black));
|
|
476
|
-
}
|
|
477
|
-
|
|
478
320
|
.divider {
|
|
479
321
|
margin: var(--spaceL) 0;
|
|
480
322
|
text-align: center;
|
|
@@ -2,6 +2,7 @@ import { useState, useEffect, useContext } from 'react';
|
|
|
2
2
|
import styles from './case-export.module.css';
|
|
3
3
|
import config from '~/config/config.json';
|
|
4
4
|
import { AuthContext } from '~/contexts/auth.context';
|
|
5
|
+
import { PublicSigningKeyModal } from '~/components/public-signing-key-modal/public-signing-key-modal';
|
|
5
6
|
import { getVerificationPublicKey } from '~/utils/signature-utils';
|
|
6
7
|
import { getCaseConfirmations, exportConfirmationData } from '../../actions/confirm-export';
|
|
7
8
|
|
|
@@ -86,8 +87,6 @@ export const CaseExport = ({
|
|
|
86
87
|
const [includeImages, setIncludeImages] = useState(false);
|
|
87
88
|
const [hasConfirmationData, setHasConfirmationData] = useState(false);
|
|
88
89
|
const [isPublicKeyModalOpen, setIsPublicKeyModalOpen] = useState(false);
|
|
89
|
-
const [isCopyingPublicKey, setIsCopyingPublicKey] = useState(false);
|
|
90
|
-
const [publicKeyCopyMessage, setPublicKeyCopyMessage] = useState('');
|
|
91
90
|
const { keyId: publicSigningKeyId, publicKeyPem } = getPublicSigningKeyDetails();
|
|
92
91
|
|
|
93
92
|
// Update caseNumber when currentCaseNumber prop changes
|
|
@@ -153,22 +152,10 @@ export const CaseExport = ({
|
|
|
153
152
|
}
|
|
154
153
|
}, [isOpen]);
|
|
155
154
|
|
|
156
|
-
useEffect(() => {
|
|
157
|
-
if (!isPublicKeyModalOpen) {
|
|
158
|
-
setIsCopyingPublicKey(false);
|
|
159
|
-
setPublicKeyCopyMessage('');
|
|
160
|
-
}
|
|
161
|
-
}, [isPublicKeyModalOpen]);
|
|
162
|
-
|
|
163
155
|
// Handle Escape key to close modal
|
|
164
156
|
useEffect(() => {
|
|
165
157
|
const handleEscapeKey = (event: KeyboardEvent) => {
|
|
166
|
-
if (event.key === 'Escape' && isOpen) {
|
|
167
|
-
if (isPublicKeyModalOpen) {
|
|
168
|
-
setIsPublicKeyModalOpen(false);
|
|
169
|
-
return;
|
|
170
|
-
}
|
|
171
|
-
|
|
158
|
+
if (event.key === 'Escape' && isOpen && !isPublicKeyModalOpen) {
|
|
172
159
|
onClose();
|
|
173
160
|
}
|
|
174
161
|
};
|
|
@@ -261,81 +248,6 @@ export const CaseExport = ({
|
|
|
261
248
|
}
|
|
262
249
|
};
|
|
263
250
|
|
|
264
|
-
const handlePublicKeyOverlayMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
|
|
265
|
-
if (e.target === e.currentTarget) {
|
|
266
|
-
setIsPublicKeyModalOpen(false);
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
const handlePublicKeyOverlayKeyDown = (e: React.KeyboardEvent<HTMLDivElement>) => {
|
|
271
|
-
if (e.target !== e.currentTarget) {
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
276
|
-
e.preventDefault();
|
|
277
|
-
setIsPublicKeyModalOpen(false);
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
|
|
281
|
-
const copyTextWithExecCommand = (text: string): boolean => {
|
|
282
|
-
const tempTextarea = document.createElement('textarea');
|
|
283
|
-
tempTextarea.value = text;
|
|
284
|
-
tempTextarea.setAttribute('readonly', '');
|
|
285
|
-
tempTextarea.style.position = 'fixed';
|
|
286
|
-
tempTextarea.style.opacity = '0';
|
|
287
|
-
tempTextarea.style.pointerEvents = 'none';
|
|
288
|
-
|
|
289
|
-
document.body.appendChild(tempTextarea);
|
|
290
|
-
tempTextarea.select();
|
|
291
|
-
|
|
292
|
-
let copied = false;
|
|
293
|
-
try {
|
|
294
|
-
copied = document.execCommand('copy');
|
|
295
|
-
} finally {
|
|
296
|
-
document.body.removeChild(tempTextarea);
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
return copied;
|
|
300
|
-
};
|
|
301
|
-
|
|
302
|
-
const handleCopyPublicKey = async () => {
|
|
303
|
-
if (!publicKeyPem) {
|
|
304
|
-
setPublicKeyCopyMessage('No public signing key is configured for this environment.');
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
setIsCopyingPublicKey(true);
|
|
309
|
-
setPublicKeyCopyMessage('');
|
|
310
|
-
|
|
311
|
-
try {
|
|
312
|
-
if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
|
|
313
|
-
await navigator.clipboard.writeText(publicKeyPem);
|
|
314
|
-
setPublicKeyCopyMessage('Public key copied to clipboard.');
|
|
315
|
-
} else {
|
|
316
|
-
const copied = copyTextWithExecCommand(publicKeyPem);
|
|
317
|
-
setPublicKeyCopyMessage(
|
|
318
|
-
copied
|
|
319
|
-
? 'Public key copied to clipboard.'
|
|
320
|
-
: 'Copy failed. Select and copy the key manually.'
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
} catch (copyError) {
|
|
324
|
-
const copied = copyTextWithExecCommand(publicKeyPem);
|
|
325
|
-
setPublicKeyCopyMessage(
|
|
326
|
-
copied
|
|
327
|
-
? 'Public key copied to clipboard.'
|
|
328
|
-
: 'Copy failed. Select and copy the key manually.'
|
|
329
|
-
);
|
|
330
|
-
|
|
331
|
-
if (!copied) {
|
|
332
|
-
console.error('Failed to copy public signing key:', copyError);
|
|
333
|
-
}
|
|
334
|
-
} finally {
|
|
335
|
-
setIsCopyingPublicKey(false);
|
|
336
|
-
}
|
|
337
|
-
};
|
|
338
|
-
|
|
339
251
|
return (
|
|
340
252
|
<div
|
|
341
253
|
className={styles.overlay}
|
|
@@ -497,101 +409,12 @@ export const CaseExport = ({
|
|
|
497
409
|
</div>
|
|
498
410
|
</div>
|
|
499
411
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
tabIndex={0}
|
|
507
|
-
aria-label="Close public signing key dialog"
|
|
508
|
-
>
|
|
509
|
-
<div
|
|
510
|
-
className={styles.publicKeyModal}
|
|
511
|
-
role="dialog"
|
|
512
|
-
aria-modal="true"
|
|
513
|
-
aria-labelledby="publicSigningKeyTitle"
|
|
514
|
-
>
|
|
515
|
-
<div className={styles.publicKeyHeader}>
|
|
516
|
-
<h3 id="publicSigningKeyTitle" className={styles.publicKeyTitle}>
|
|
517
|
-
Striae Public Signing Key
|
|
518
|
-
</h3>
|
|
519
|
-
<button
|
|
520
|
-
type="button"
|
|
521
|
-
className={styles.closeButton}
|
|
522
|
-
onClick={() => setIsPublicKeyModalOpen(false)}
|
|
523
|
-
aria-label="Close public signing key dialog"
|
|
524
|
-
>
|
|
525
|
-
×
|
|
526
|
-
</button>
|
|
527
|
-
</div>
|
|
528
|
-
|
|
529
|
-
<div className={styles.publicKeyContent}>
|
|
530
|
-
<p className={styles.publicKeyDescription}>
|
|
531
|
-
This key verifies digital signatures attached to Striae exports. It is safe to share for
|
|
532
|
-
independent verification.
|
|
533
|
-
</p>
|
|
534
|
-
|
|
535
|
-
{publicSigningKeyId && (
|
|
536
|
-
<p className={styles.publicKeyMeta}>
|
|
537
|
-
Key ID: <span>{publicSigningKeyId}</span>
|
|
538
|
-
</p>
|
|
539
|
-
)}
|
|
540
|
-
|
|
541
|
-
<label htmlFor="publicSigningKey" className={styles.publicKeyLabel}>
|
|
542
|
-
Public signing key (PEM)
|
|
543
|
-
</label>
|
|
544
|
-
<textarea
|
|
545
|
-
id="publicSigningKey"
|
|
546
|
-
className={styles.publicKeyField}
|
|
547
|
-
value={publicKeyPem || 'No public signing key is configured for this environment.'}
|
|
548
|
-
readOnly
|
|
549
|
-
rows={10}
|
|
550
|
-
/>
|
|
551
|
-
|
|
552
|
-
<p className={styles.publicKeyHowToTitle}>How to verify Striae exports</p>
|
|
553
|
-
<ol className={styles.publicKeyHowToList}>
|
|
554
|
-
<li>
|
|
555
|
-
Locate signature metadata in the export (for case ZIP exports, see FORENSIC_MANIFEST.json;
|
|
556
|
-
for confirmation exports, see metadata.signature).
|
|
557
|
-
</li>
|
|
558
|
-
<li>
|
|
559
|
-
Use this public key with your signature verification workflow (for example OpenSSL or an
|
|
560
|
-
internal verifier) to validate the signed payload.
|
|
561
|
-
</li>
|
|
562
|
-
<li>
|
|
563
|
-
Trust the export only when signature verification succeeds and the key ID matches the export
|
|
564
|
-
metadata.
|
|
565
|
-
</li>
|
|
566
|
-
</ol>
|
|
567
|
-
|
|
568
|
-
{publicKeyCopyMessage && (
|
|
569
|
-
<p className={styles.publicKeyStatus} role="status" aria-live="polite">
|
|
570
|
-
{publicKeyCopyMessage}
|
|
571
|
-
</p>
|
|
572
|
-
)}
|
|
573
|
-
|
|
574
|
-
<div className={styles.publicKeyActions}>
|
|
575
|
-
<button
|
|
576
|
-
type="button"
|
|
577
|
-
className={styles.publicKeyCopyButton}
|
|
578
|
-
onClick={handleCopyPublicKey}
|
|
579
|
-
disabled={isCopyingPublicKey || !publicKeyPem}
|
|
580
|
-
>
|
|
581
|
-
{isCopyingPublicKey ? 'Copying...' : 'Copy Key'}
|
|
582
|
-
</button>
|
|
583
|
-
<button
|
|
584
|
-
type="button"
|
|
585
|
-
className={styles.publicKeyCloseButton}
|
|
586
|
-
onClick={() => setIsPublicKeyModalOpen(false)}
|
|
587
|
-
>
|
|
588
|
-
Close
|
|
589
|
-
</button>
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
</div>
|
|
593
|
-
</div>
|
|
594
|
-
)}
|
|
412
|
+
<PublicSigningKeyModal
|
|
413
|
+
isOpen={isPublicKeyModalOpen}
|
|
414
|
+
onClose={() => setIsPublicKeyModalOpen(false)}
|
|
415
|
+
publicSigningKeyId={publicSigningKeyId}
|
|
416
|
+
publicKeyPem={publicKeyPem}
|
|
417
|
+
/>
|
|
595
418
|
</div>
|
|
596
419
|
);
|
|
597
420
|
};
|
|
@@ -557,7 +557,7 @@ Audit Signature: ${JSON.stringify(signaturePayload.signature)}
|
|
|
557
557
|
Verification Instructions:
|
|
558
558
|
1. Copy the entire report content above the "INTEGRITY VERIFICATION" section
|
|
559
559
|
2. Calculate SHA256 hash of that content (excluding this verification section)
|
|
560
|
-
3. Validate audit signature metadata and signature with
|
|
560
|
+
3. Validate audit signature metadata and signature with your signature verification workflow (for example OpenSSL or an internal verifier)
|
|
561
561
|
4. Confirm both hash and signature validation pass before relying on this report
|
|
562
562
|
|
|
563
563
|
This report requires both hash and signature validation for tamper detection.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@striae-org/striae",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Cloud-native forensic annotation application for firearms identification (Remix + Cloudflare Workers).",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"publish:all": "npm run publish:npm && npm run publish:github",
|
|
80
80
|
"publish:all:dry-run": "npm run publish:npm:dry-run && npm run publish:github:dry-run",
|
|
81
81
|
"lint": "node ./scripts/run-eslint.cjs",
|
|
82
|
-
"start": "node ./scripts/dev.cjs && wrangler pages dev
|
|
82
|
+
"start": "node ./scripts/dev.cjs && wrangler pages dev",
|
|
83
83
|
"typecheck": "tsc",
|
|
84
84
|
"typegen": "wrangler types",
|
|
85
85
|
"preview": "npm run build && wrangler pages dev",
|
|
@@ -130,6 +130,9 @@
|
|
|
130
130
|
"vite-tsconfig-paths": "^6.1.1",
|
|
131
131
|
"wrangler": "^3.114.17"
|
|
132
132
|
},
|
|
133
|
+
"overrides": {
|
|
134
|
+
"tar": "7.5.11"
|
|
135
|
+
},
|
|
133
136
|
"engines": {
|
|
134
137
|
"node": ">=20.0.0"
|
|
135
138
|
},
|
package/scripts/deploy-pages.sh
CHANGED
|
@@ -27,8 +27,8 @@ fi
|
|
|
27
27
|
echo -e "${GREEN}✅ Pages deployment completed successfully${NC}"
|
|
28
28
|
|
|
29
29
|
echo -e "\n${BLUE}💡 Next Steps:${NC}"
|
|
30
|
-
echo " 1.
|
|
30
|
+
echo " 1. Test your application"
|
|
31
31
|
echo " 2. Configure custom domain (optional)"
|
|
32
|
-
echo " 3.
|
|
32
|
+
echo " 3. Verify Pages environment variables in Cloudflare dashboard"
|
|
33
33
|
|
|
34
34
|
echo -e "\n${GREEN}✨ Pages deployment complete!${NC}"
|
package/wrangler.toml.example
CHANGED