@saluzi/saluzi-edu 0.2.12 → 0.2.13

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 (167) hide show
  1. package/dist/cli.js +212 -140
  2. package/dist/rcs-web/assets/Dashboard-YYwDrKTN.js +1 -0
  3. package/dist/rcs-web/assets/Join-DKCP5X6X.js +1 -0
  4. package/dist/rcs-web/assets/Login-0VGZs85I.js +1 -0
  5. package/{packages/remote-control-server/web/dist/assets/SessionDetail-DSoMXZJb.js → dist/rcs-web/assets/SessionDetail-DmK-DfBQ.js} +11 -11
  6. package/dist/rcs-web/assets/Setup-C0cjxXMt.js +1 -0
  7. package/dist/rcs-web/assets/{ShareLanding-C06U9Nrr.js → ShareLanding-DUZrreJ7.js} +1 -1
  8. package/dist/rcs-web/assets/TeamCreate-B8ZV6EdY.js +1 -0
  9. package/dist/rcs-web/assets/{TeamDetail-d_GwIRHd.js → TeamDetail-DBAsJ__i.js} +2 -2
  10. package/dist/rcs-web/assets/{TeamList-BlNGdKci.js → TeamList-C-Hte4nV.js} +1 -1
  11. package/dist/rcs-web/assets/{TeamSettings-CPraC9uS.js → TeamSettings-CNVwYwcp.js} +1 -1
  12. package/dist/rcs-web/assets/UserSettings-DRdz9kYE.js +1 -0
  13. package/dist/rcs-web/assets/{arc-BmYza-0n.js → arc-CJxirXEQ.js} +1 -1
  14. package/dist/rcs-web/assets/{architectureDiagram-3BPJPVTR-CYBtlgdO.js → architectureDiagram-3BPJPVTR-BZvIHrLA.js} +1 -1
  15. package/dist/rcs-web/assets/{blockDiagram-GPEHLZMM-RJAfHk_a.js → blockDiagram-GPEHLZMM-3p0y3Ija.js} +1 -1
  16. package/{packages/remote-control-server/web/dist/assets/c4Diagram-AAUBKEIU-CJ-qJA6w.js → dist/rcs-web/assets/c4Diagram-AAUBKEIU-Bmz9A1wn.js} +1 -1
  17. package/dist/rcs-web/assets/channel-DaI_n8dp.js +1 -0
  18. package/dist/rcs-web/assets/{chunk-2J33WTMH-n5I6Q1zJ.js → chunk-2J33WTMH-B8gQz22j.js} +1 -1
  19. package/dist/rcs-web/assets/{chunk-4BX2VUAB-CSOBh7qY.js → chunk-4BX2VUAB-CgLEZsTU.js} +1 -1
  20. package/dist/rcs-web/assets/{chunk-55IACEB6-C0euQVm_.js → chunk-55IACEB6-0mdE_-IS.js} +1 -1
  21. package/{packages/remote-control-server/web/dist/assets/chunk-727SXJPM-Ch7yD8cR.js → dist/rcs-web/assets/chunk-727SXJPM-DdtjnIHR.js} +1 -1
  22. package/{packages/remote-control-server/web/dist/assets/chunk-AQP2D5EJ-Ms2Q7r6u.js → dist/rcs-web/assets/chunk-AQP2D5EJ-BtwFPk50.js} +1 -1
  23. package/dist/rcs-web/assets/{chunk-FMBD7UC4-bszHUkpx.js → chunk-FMBD7UC4-BcO9mQ0P.js} +1 -1
  24. package/dist/rcs-web/assets/{chunk-ND2GUHAM-BVQIhq6J.js → chunk-ND2GUHAM-BvRq13S_.js} +1 -1
  25. package/dist/rcs-web/assets/{chunk-QZHKN3VN-BVEDe5qc.js → chunk-QZHKN3VN-D7GHkPpq.js} +1 -1
  26. package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-pYllpkOq.js +1 -0
  27. package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-pYllpkOq.js +1 -0
  28. package/dist/rcs-web/assets/{code-block-IT6T5CEO-B26n-rFC.js → code-block-IT6T5CEO-B1p-vO-3.js} +1 -1
  29. package/{packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-DXc7GE8W.js → dist/rcs-web/assets/cose-bilkent-S5V4N54A-L0rUZuH5.js} +1 -1
  30. package/dist/rcs-web/assets/{dagre-BM42HDAG-DAXofHt_.js → dagre-BM42HDAG-CHGuio22.js} +1 -1
  31. package/dist/rcs-web/assets/{diagram-2AECGRRQ-DK0TBUQA.js → diagram-2AECGRRQ-DIDRyQk8.js} +1 -1
  32. package/dist/rcs-web/assets/{diagram-5GNKFQAL-DMsq-Grp.js → diagram-5GNKFQAL-CNubKb6P.js} +1 -1
  33. package/dist/rcs-web/assets/{diagram-KO2AKTUF-B3FMhmUc.js → diagram-KO2AKTUF-CzQh664-.js} +1 -1
  34. package/dist/rcs-web/assets/{diagram-LMA3HP47-BjO3BHFl.js → diagram-LMA3HP47-CWBR7-VG.js} +1 -1
  35. package/dist/rcs-web/assets/{diagram-OG6HWLK6-CNg4B2L3.js → diagram-OG6HWLK6-BMHdlbcZ.js} +1 -1
  36. package/{packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-BDxz8NRK.js → dist/rcs-web/assets/erDiagram-TEJ5UH35-OgT8dC-K.js} +1 -1
  37. package/{packages/remote-control-server/web/dist/assets/flowDiagram-I6XJVG4X-VIgM13sK.js → dist/rcs-web/assets/flowDiagram-I6XJVG4X-DRrCY2oB.js} +1 -1
  38. package/dist/rcs-web/assets/{ganttDiagram-6RSMTGT7-Ba2QuJU0.js → ganttDiagram-6RSMTGT7-DctpzEKN.js} +1 -1
  39. package/dist/rcs-web/assets/{gitGraphDiagram-PVQCEYII-B-gV-lAg.js → gitGraphDiagram-PVQCEYII-5KpLszm9.js} +1 -1
  40. package/dist/rcs-web/assets/{infoDiagram-5YYISTIA-DothwgD4.js → infoDiagram-5YYISTIA-BAKD_Tt7.js} +1 -1
  41. package/{packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-DhpjM1Vc.js → dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-VHOPG4Nc.js} +1 -1
  42. package/dist/rcs-web/assets/{journeyDiagram-JHISSGLW-PbQTouMJ.js → journeyDiagram-JHISSGLW-BtGih-ik.js} +1 -1
  43. package/dist/rcs-web/assets/{kanban-definition-UN3LZRKU-63WdwG-X.js → kanban-definition-UN3LZRKU-BsEGZsD1.js} +1 -1
  44. package/dist/rcs-web/assets/{linear-CeRljV9s.js → linear-Dj636PNG.js} +1 -1
  45. package/dist/rcs-web/assets/main-CkqtCllF.js +238 -0
  46. package/dist/rcs-web/assets/{mermaid-VLURNSYL-CG6yNR7g.js → mermaid-VLURNSYL-DutpHHm9.js} +4 -4
  47. package/dist/rcs-web/assets/{mermaid.core-CAuNNATY.js → mermaid.core-XHHf0pen.js} +5 -5
  48. package/dist/rcs-web/assets/{mindmap-definition-RKZ34NQL-9hDL1CUV.js → mindmap-definition-RKZ34NQL-BBa8V_3g.js} +1 -1
  49. package/dist/rcs-web/assets/{pieDiagram-4H26LBE5-BS_P2t04.js → pieDiagram-4H26LBE5-BuNEdo9H.js} +1 -1
  50. package/dist/rcs-web/assets/{quadrantDiagram-W4KKPZXB-Cp9QQkHV.js → quadrantDiagram-W4KKPZXB-CsJgyQIN.js} +1 -1
  51. package/dist/rcs-web/assets/{requirementDiagram-4Y6WPE33-C_XSzOKE.js → requirementDiagram-4Y6WPE33-DqMJZ914.js} +1 -1
  52. package/{packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-BhuVqzZd.js → dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-CVsz09q2.js} +1 -1
  53. package/{packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-C3IPZrcW.js → dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-BRp4Wddn.js} +1 -1
  54. package/dist/rcs-web/assets/{stateDiagram-AJRCARHV-CN4iNPRl.js → stateDiagram-AJRCARHV-S-QqQr-D.js} +1 -1
  55. package/dist/rcs-web/assets/{stateDiagram-v2-BHNVJYJU-CICBlDSJ.js → stateDiagram-v2-BHNVJYJU-D77KfDr-.js} +1 -1
  56. package/{packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-Be7t_3rC.js → dist/rcs-web/assets/timeline-definition-PNZ67QCA-De8bFWnE.js} +1 -1
  57. package/dist/rcs-web/assets/{useTeams-DPHGnBHL.js → useTeams-CqJOqEy1.js} +1 -1
  58. package/dist/rcs-web/assets/{vennDiagram-CIIHVFJN-CSaUc2Xv.js → vennDiagram-CIIHVFJN-Dikwrn1L.js} +1 -1
  59. package/dist/rcs-web/assets/{wardleyDiagram-YWT4CUSO-DTuJrl9P.js → wardleyDiagram-YWT4CUSO-DUzpiV6H.js} +1 -1
  60. package/dist/rcs-web/assets/{workerType-CSqeySYe.js → workerType-DOQ0AHrq.js} +1 -1
  61. package/dist/rcs-web/assets/{xychartDiagram-2RQKCTM6-BQ0oU3Qi.js → xychartDiagram-2RQKCTM6-OZNbB32D.js} +1 -1
  62. package/dist/rcs-web/index.html +1 -1
  63. package/package.json +2 -2
  64. package/packages/remote-control-server/src/__tests__/cookie.test.ts +0 -71
  65. package/packages/remote-control-server/src/__tests__/data-layer-round2.test.ts +556 -0
  66. package/packages/remote-control-server/src/__tests__/route-mounting.test.ts +212 -0
  67. package/packages/remote-control-server/src/__tests__/routes.test.ts +5 -44
  68. package/packages/remote-control-server/src/__tests__/security-fixes.test.ts +526 -0
  69. package/packages/remote-control-server/src/__tests__/security-round3.test.ts +780 -0
  70. package/packages/remote-control-server/src/__tests__/shares.test.ts +5 -2
  71. package/packages/remote-control-server/src/auth/constants.ts +0 -6
  72. package/packages/remote-control-server/src/auth/cookie.ts +0 -43
  73. package/packages/remote-control-server/src/auth/csrf.ts +58 -0
  74. package/packages/remote-control-server/src/auth/middleware.ts +7 -4
  75. package/packages/remote-control-server/src/index.ts +26 -0
  76. package/packages/remote-control-server/src/registerRoutes.ts +17 -1
  77. package/packages/remote-control-server/src/routes/web/auth-routes.ts +93 -54
  78. package/packages/remote-control-server/src/routes/web/auth.ts +16 -23
  79. package/packages/remote-control-server/src/routes/web/sessions.ts +30 -5
  80. package/packages/remote-control-server/src/routes/web/shares.ts +38 -1
  81. package/packages/remote-control-server/src/routes/web/teams.ts +22 -4
  82. package/packages/remote-control-server/src/services/session.ts +143 -9
  83. package/packages/remote-control-server/src/store.ts +34 -1
  84. package/packages/remote-control-server/src/types/api.ts +0 -1
  85. package/packages/remote-control-server/web/dist/assets/Dashboard-YYwDrKTN.js +1 -0
  86. package/packages/remote-control-server/web/dist/assets/Join-DKCP5X6X.js +1 -0
  87. package/packages/remote-control-server/web/dist/assets/Login-0VGZs85I.js +1 -0
  88. package/{dist/rcs-web/assets/SessionDetail-DSoMXZJb.js → packages/remote-control-server/web/dist/assets/SessionDetail-DmK-DfBQ.js} +11 -11
  89. package/packages/remote-control-server/web/dist/assets/Setup-C0cjxXMt.js +1 -0
  90. package/packages/remote-control-server/web/dist/assets/{ShareLanding-C06U9Nrr.js → ShareLanding-DUZrreJ7.js} +1 -1
  91. package/packages/remote-control-server/web/dist/assets/TeamCreate-B8ZV6EdY.js +1 -0
  92. package/packages/remote-control-server/web/dist/assets/{TeamDetail-d_GwIRHd.js → TeamDetail-DBAsJ__i.js} +2 -2
  93. package/packages/remote-control-server/web/dist/assets/{TeamList-BlNGdKci.js → TeamList-C-Hte4nV.js} +1 -1
  94. package/packages/remote-control-server/web/dist/assets/{TeamSettings-CPraC9uS.js → TeamSettings-CNVwYwcp.js} +1 -1
  95. package/packages/remote-control-server/web/dist/assets/UserSettings-DRdz9kYE.js +1 -0
  96. package/packages/remote-control-server/web/dist/assets/{arc-BmYza-0n.js → arc-CJxirXEQ.js} +1 -1
  97. package/packages/remote-control-server/web/dist/assets/{architectureDiagram-3BPJPVTR-CYBtlgdO.js → architectureDiagram-3BPJPVTR-BZvIHrLA.js} +1 -1
  98. package/packages/remote-control-server/web/dist/assets/{blockDiagram-GPEHLZMM-RJAfHk_a.js → blockDiagram-GPEHLZMM-3p0y3Ija.js} +1 -1
  99. package/{dist/rcs-web/assets/c4Diagram-AAUBKEIU-CJ-qJA6w.js → packages/remote-control-server/web/dist/assets/c4Diagram-AAUBKEIU-Bmz9A1wn.js} +1 -1
  100. package/packages/remote-control-server/web/dist/assets/channel-DaI_n8dp.js +1 -0
  101. package/packages/remote-control-server/web/dist/assets/{chunk-2J33WTMH-n5I6Q1zJ.js → chunk-2J33WTMH-B8gQz22j.js} +1 -1
  102. package/packages/remote-control-server/web/dist/assets/{chunk-4BX2VUAB-CSOBh7qY.js → chunk-4BX2VUAB-CgLEZsTU.js} +1 -1
  103. package/packages/remote-control-server/web/dist/assets/{chunk-55IACEB6-C0euQVm_.js → chunk-55IACEB6-0mdE_-IS.js} +1 -1
  104. package/{dist/rcs-web/assets/chunk-727SXJPM-Ch7yD8cR.js → packages/remote-control-server/web/dist/assets/chunk-727SXJPM-DdtjnIHR.js} +1 -1
  105. package/{dist/rcs-web/assets/chunk-AQP2D5EJ-Ms2Q7r6u.js → packages/remote-control-server/web/dist/assets/chunk-AQP2D5EJ-BtwFPk50.js} +1 -1
  106. package/packages/remote-control-server/web/dist/assets/{chunk-FMBD7UC4-bszHUkpx.js → chunk-FMBD7UC4-BcO9mQ0P.js} +1 -1
  107. package/packages/remote-control-server/web/dist/assets/{chunk-ND2GUHAM-BVQIhq6J.js → chunk-ND2GUHAM-BvRq13S_.js} +1 -1
  108. package/packages/remote-control-server/web/dist/assets/{chunk-QZHKN3VN-BVEDe5qc.js → chunk-QZHKN3VN-D7GHkPpq.js} +1 -1
  109. package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-pYllpkOq.js +1 -0
  110. package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-pYllpkOq.js +1 -0
  111. package/packages/remote-control-server/web/dist/assets/{code-block-IT6T5CEO-B26n-rFC.js → code-block-IT6T5CEO-B1p-vO-3.js} +1 -1
  112. package/{dist/rcs-web/assets/cose-bilkent-S5V4N54A-DXc7GE8W.js → packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-L0rUZuH5.js} +1 -1
  113. package/packages/remote-control-server/web/dist/assets/{dagre-BM42HDAG-DAXofHt_.js → dagre-BM42HDAG-CHGuio22.js} +1 -1
  114. package/packages/remote-control-server/web/dist/assets/{diagram-2AECGRRQ-DK0TBUQA.js → diagram-2AECGRRQ-DIDRyQk8.js} +1 -1
  115. package/packages/remote-control-server/web/dist/assets/{diagram-5GNKFQAL-DMsq-Grp.js → diagram-5GNKFQAL-CNubKb6P.js} +1 -1
  116. package/packages/remote-control-server/web/dist/assets/{diagram-KO2AKTUF-B3FMhmUc.js → diagram-KO2AKTUF-CzQh664-.js} +1 -1
  117. package/packages/remote-control-server/web/dist/assets/{diagram-LMA3HP47-BjO3BHFl.js → diagram-LMA3HP47-CWBR7-VG.js} +1 -1
  118. package/packages/remote-control-server/web/dist/assets/{diagram-OG6HWLK6-CNg4B2L3.js → diagram-OG6HWLK6-BMHdlbcZ.js} +1 -1
  119. package/{dist/rcs-web/assets/erDiagram-TEJ5UH35-BDxz8NRK.js → packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-OgT8dC-K.js} +1 -1
  120. package/{dist/rcs-web/assets/flowDiagram-I6XJVG4X-VIgM13sK.js → packages/remote-control-server/web/dist/assets/flowDiagram-I6XJVG4X-DRrCY2oB.js} +1 -1
  121. package/packages/remote-control-server/web/dist/assets/{ganttDiagram-6RSMTGT7-Ba2QuJU0.js → ganttDiagram-6RSMTGT7-DctpzEKN.js} +1 -1
  122. package/packages/remote-control-server/web/dist/assets/{gitGraphDiagram-PVQCEYII-B-gV-lAg.js → gitGraphDiagram-PVQCEYII-5KpLszm9.js} +1 -1
  123. package/packages/remote-control-server/web/dist/assets/{infoDiagram-5YYISTIA-DothwgD4.js → infoDiagram-5YYISTIA-BAKD_Tt7.js} +1 -1
  124. package/{dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-DhpjM1Vc.js → packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-VHOPG4Nc.js} +1 -1
  125. package/packages/remote-control-server/web/dist/assets/{journeyDiagram-JHISSGLW-PbQTouMJ.js → journeyDiagram-JHISSGLW-BtGih-ik.js} +1 -1
  126. package/packages/remote-control-server/web/dist/assets/{kanban-definition-UN3LZRKU-63WdwG-X.js → kanban-definition-UN3LZRKU-BsEGZsD1.js} +1 -1
  127. package/packages/remote-control-server/web/dist/assets/{linear-CeRljV9s.js → linear-Dj636PNG.js} +1 -1
  128. package/packages/remote-control-server/web/dist/assets/main-CkqtCllF.js +238 -0
  129. package/packages/remote-control-server/web/dist/assets/{mermaid-VLURNSYL-CG6yNR7g.js → mermaid-VLURNSYL-DutpHHm9.js} +4 -4
  130. package/packages/remote-control-server/web/dist/assets/{mermaid.core-CAuNNATY.js → mermaid.core-XHHf0pen.js} +5 -5
  131. package/packages/remote-control-server/web/dist/assets/{mindmap-definition-RKZ34NQL-9hDL1CUV.js → mindmap-definition-RKZ34NQL-BBa8V_3g.js} +1 -1
  132. package/packages/remote-control-server/web/dist/assets/{pieDiagram-4H26LBE5-BS_P2t04.js → pieDiagram-4H26LBE5-BuNEdo9H.js} +1 -1
  133. package/packages/remote-control-server/web/dist/assets/{quadrantDiagram-W4KKPZXB-Cp9QQkHV.js → quadrantDiagram-W4KKPZXB-CsJgyQIN.js} +1 -1
  134. package/packages/remote-control-server/web/dist/assets/{requirementDiagram-4Y6WPE33-C_XSzOKE.js → requirementDiagram-4Y6WPE33-DqMJZ914.js} +1 -1
  135. package/{dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-BhuVqzZd.js → packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-CVsz09q2.js} +1 -1
  136. package/{dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-C3IPZrcW.js → packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-BRp4Wddn.js} +1 -1
  137. package/packages/remote-control-server/web/dist/assets/{stateDiagram-AJRCARHV-CN4iNPRl.js → stateDiagram-AJRCARHV-S-QqQr-D.js} +1 -1
  138. package/packages/remote-control-server/web/dist/assets/{stateDiagram-v2-BHNVJYJU-CICBlDSJ.js → stateDiagram-v2-BHNVJYJU-D77KfDr-.js} +1 -1
  139. package/{dist/rcs-web/assets/timeline-definition-PNZ67QCA-Be7t_3rC.js → packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-De8bFWnE.js} +1 -1
  140. package/packages/remote-control-server/web/dist/assets/{useTeams-DPHGnBHL.js → useTeams-CqJOqEy1.js} +1 -1
  141. package/packages/remote-control-server/web/dist/assets/{vennDiagram-CIIHVFJN-CSaUc2Xv.js → vennDiagram-CIIHVFJN-Dikwrn1L.js} +1 -1
  142. package/packages/remote-control-server/web/dist/assets/{wardleyDiagram-YWT4CUSO-DTuJrl9P.js → wardleyDiagram-YWT4CUSO-DUzpiV6H.js} +1 -1
  143. package/packages/remote-control-server/web/dist/assets/{workerType-CSqeySYe.js → workerType-DOQ0AHrq.js} +1 -1
  144. package/packages/remote-control-server/web/dist/assets/{xychartDiagram-2RQKCTM6-BQ0oU3Qi.js → xychartDiagram-2RQKCTM6-OZNbB32D.js} +1 -1
  145. package/packages/remote-control-server/web/dist/index.html +1 -1
  146. package/dist/rcs-web/assets/Dashboard-D_Im4UId.js +0 -1
  147. package/dist/rcs-web/assets/Join-BTnvMsB9.js +0 -1
  148. package/dist/rcs-web/assets/Login-ClT7RckC.js +0 -1
  149. package/dist/rcs-web/assets/Setup-PaDA6Et-.js +0 -1
  150. package/dist/rcs-web/assets/TeamCreate-249rK3U3.js +0 -1
  151. package/dist/rcs-web/assets/UserSettings-X627Fdni.js +0 -1
  152. package/dist/rcs-web/assets/channel-BUwWbT6U.js +0 -1
  153. package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-DYtx22zv.js +0 -1
  154. package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-DYtx22zv.js +0 -1
  155. package/dist/rcs-web/assets/i18n-Dbt-vEQ3.js +0 -1
  156. package/dist/rcs-web/assets/main-DvfxciBy.js +0 -238
  157. package/packages/remote-control-server/web/dist/assets/Dashboard-D_Im4UId.js +0 -1
  158. package/packages/remote-control-server/web/dist/assets/Join-BTnvMsB9.js +0 -1
  159. package/packages/remote-control-server/web/dist/assets/Login-ClT7RckC.js +0 -1
  160. package/packages/remote-control-server/web/dist/assets/Setup-PaDA6Et-.js +0 -1
  161. package/packages/remote-control-server/web/dist/assets/TeamCreate-249rK3U3.js +0 -1
  162. package/packages/remote-control-server/web/dist/assets/UserSettings-X627Fdni.js +0 -1
  163. package/packages/remote-control-server/web/dist/assets/channel-BUwWbT6U.js +0 -1
  164. package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-DYtx22zv.js +0 -1
  165. package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-DYtx22zv.js +0 -1
  166. package/packages/remote-control-server/web/dist/assets/i18n-Dbt-vEQ3.js +0 -1
  167. package/packages/remote-control-server/web/dist/assets/main-DvfxciBy.js +0 -238
@@ -0,0 +1,526 @@
1
+ import { describe, test, expect, beforeEach, mock } from 'bun:test'
2
+ import { randomUUID, createHash } from 'node:crypto'
3
+ import { mockConfigModule } from './helpers/mock-config'
4
+
5
+ // Mock config before imports
6
+ mock.module('../config', () => mockConfigModule())
7
+
8
+ import { Hono } from 'hono'
9
+
10
+ let authRoutes: Hono | undefined
11
+ let webAuth: Hono | undefined
12
+ let resetDbSingleton: (() => void) | undefined
13
+ let getDb: (() => import('bun:sqlite').Database) | undefined
14
+ let resetRateLimits: (() => void) | undefined
15
+
16
+ try {
17
+ const dbMod = await import('../db/sqlite')
18
+ getDb = dbMod.getDb
19
+ resetDbSingleton = dbMod.resetDbSingleton
20
+ } catch {
21
+ // Module may not exist yet
22
+ }
23
+
24
+ try {
25
+ const mod = await import('../routes/web/auth-routes')
26
+ authRoutes = mod.default
27
+ resetRateLimits = mod._resetRateLimits
28
+ } catch {
29
+ // Module may not exist yet
30
+ }
31
+
32
+ try {
33
+ const mod = await import('../routes/web/auth')
34
+ webAuth = mod.default
35
+ } catch {
36
+ // Module may not exist yet
37
+ }
38
+
39
+ function freshDb() {
40
+ if (resetDbSingleton) {
41
+ try {
42
+ resetDbSingleton()
43
+ } catch {
44
+ // ignore
45
+ }
46
+ }
47
+ if (getDb) {
48
+ const db = getDb()
49
+ try {
50
+ db.exec('DELETE FROM session_tokens')
51
+ db.exec('DELETE FROM users')
52
+ } catch {
53
+ // tables may not exist yet
54
+ }
55
+ return db
56
+ }
57
+ throw new Error('getDb not available')
58
+ }
59
+
60
+ function createApp(): Hono {
61
+ const app = new Hono()
62
+ if (webAuth) app.route('/web', webAuth)
63
+ if (authRoutes) app.route('/web/auth', authRoutes)
64
+ return app
65
+ }
66
+
67
+ // ===========================================================================
68
+ // Task 1: /web/bind returns 410 Gone
69
+ // ===========================================================================
70
+ describe('Security: /web/bind removed', () => {
71
+ let app: Hono
72
+
73
+ beforeEach(() => {
74
+ freshDb()
75
+ app = createApp()
76
+ })
77
+
78
+ test('POST /web/bind returns 410 Gone', async () => {
79
+ const res = await app.request('/web/bind', {
80
+ method: 'POST',
81
+ headers: { 'Content-Type': 'application/json' },
82
+ body: JSON.stringify({ sessionId: 'ses_123', uuid: 'some-uuid' }),
83
+ })
84
+ expect(res.status).toBe(410)
85
+ const body = await res.json()
86
+ expect(body.error).toBeDefined()
87
+ })
88
+ })
89
+
90
+ // ===========================================================================
91
+ // Task 3: /login timing side-channel mitigation
92
+ // ===========================================================================
93
+ describe('Security: /login timing side-channel', () => {
94
+ let app: Hono
95
+
96
+ beforeEach(async () => {
97
+ freshDb()
98
+ if (resetRateLimits) resetRateLimits()
99
+ app = createApp()
100
+ // Create admin via setup
101
+ await app.request('/web/auth/setup', {
102
+ method: 'POST',
103
+ headers: { 'Content-Type': 'application/json' },
104
+ body: JSON.stringify({
105
+ apiKey: 'test-api-key',
106
+ username: 'admin',
107
+ password: 'admin-password-123',
108
+ }),
109
+ })
110
+ })
111
+
112
+ test('both non-existent user and wrong password return same error message', async () => {
113
+ const resNonExistent = await app.request('/web/auth/login', {
114
+ method: 'POST',
115
+ headers: { 'Content-Type': 'application/json' },
116
+ body: JSON.stringify({
117
+ username: 'nonexistent_user',
118
+ password: 'some-password-123',
119
+ }),
120
+ })
121
+ const bodyNonExistent = await resNonExistent.json()
122
+
123
+ const resWrongPass = await app.request('/web/auth/login', {
124
+ method: 'POST',
125
+ headers: { 'Content-Type': 'application/json' },
126
+ body: JSON.stringify({
127
+ username: 'admin',
128
+ password: 'wrong-password-123',
129
+ }),
130
+ })
131
+ const bodyWrongPass = await resWrongPass.json()
132
+
133
+ // Both return 401
134
+ expect(resNonExistent.status).toBe(401)
135
+ expect(resWrongPass.status).toBe(401)
136
+
137
+ // Both return the same generic error message (does not leak user existence)
138
+ expect(bodyNonExistent.error).toBe(bodyWrongPass.error)
139
+ })
140
+
141
+ test(
142
+ 'non-existent user login takes similar time to wrong-password login',
143
+ async () => {
144
+ // Measure timing for non-existent user (should run dummy hash)
145
+ const startNonExistent = performance.now()
146
+ for (let i = 0; i < 3; i++) {
147
+ await app.request('/web/auth/login', {
148
+ method: 'POST',
149
+ headers: {
150
+ 'Content-Type': 'application/json',
151
+ 'X-Forwarded-For': `10.0.0.${10 + i}`,
152
+ },
153
+ body: JSON.stringify({
154
+ username: 'nonexistent_user_xyz',
155
+ password: 'some-password-123',
156
+ }),
157
+ })
158
+ }
159
+ const elapsedNonExistent = performance.now() - startNonExistent
160
+
161
+ // Measure timing for wrong password
162
+ const startWrongPass = performance.now()
163
+ for (let i = 0; i < 3; i++) {
164
+ await app.request('/web/auth/login', {
165
+ method: 'POST',
166
+ headers: {
167
+ 'Content-Type': 'application/json',
168
+ 'X-Forwarded-For': `10.0.1.${10 + i}`,
169
+ },
170
+ body: JSON.stringify({
171
+ username: 'admin',
172
+ password: 'wrong-password-xyz',
173
+ }),
174
+ })
175
+ }
176
+ const elapsedWrongPass = performance.now() - startWrongPass
177
+
178
+ // Non-existent user should take at least 50% of the time of wrong-password
179
+ // (both should invoke argon2id verification)
180
+ // This is a loose bound to avoid flaky tests — the point is that
181
+ // non-existent user is NOT an order of magnitude faster.
182
+ const ratio = elapsedNonExistent / elapsedWrongPass
183
+ expect(ratio).toBeGreaterThan(0.3)
184
+ },
185
+ { timeout: 60000 },
186
+ )
187
+ })
188
+
189
+ // ===========================================================================
190
+ // Task 2: consumed_at semantics — max_uses > 1 invitations
191
+ // ===========================================================================
192
+ describe('Security: consumed_at semantics for multi-use invitations', () => {
193
+ let app: Hono
194
+ let inviteToken: string
195
+
196
+ beforeEach(async () => {
197
+ freshDb()
198
+ if (resetRateLimits) resetRateLimits()
199
+ app = createApp()
200
+ // Setup admin
201
+ await app.request('/web/auth/setup', {
202
+ method: 'POST',
203
+ headers: { 'Content-Type': 'application/json' },
204
+ body: JSON.stringify({
205
+ apiKey: 'test-api-key',
206
+ username: 'admin',
207
+ password: 'admin-password-123',
208
+ }),
209
+ })
210
+
211
+ // Create a max_uses=3 invitation
212
+ const db = getDb!()
213
+ inviteToken = `inv_multi_${randomUUID().slice(0, 8)}`
214
+ const tokenHash = createHash('sha256').update(inviteToken).digest('hex')
215
+ const futureExpiry = new Date(Date.now() + 86400000).toISOString()
216
+ const adminRow = db
217
+ .query("SELECT id FROM users WHERE username = 'admin'")
218
+ .get() as { id: string }
219
+ db.exec(
220
+ `INSERT INTO invitations (token_hash, role, expires_at, max_uses, uses, created_by)
221
+ VALUES ('${tokenHash}', 'member', '${futureExpiry}', 3, 0, '${adminRow.id}')`,
222
+ )
223
+ })
224
+
225
+ test(
226
+ 'multi-use invitation (max_uses=3) allows multiple joins',
227
+ async () => {
228
+ // First join should succeed
229
+ const res1 = await app.request('/web/auth/join', {
230
+ method: 'POST',
231
+ headers: { 'Content-Type': 'application/json' },
232
+ body: JSON.stringify({
233
+ inviteToken,
234
+ username: 'user_one',
235
+ password: 'user-one-password-123',
236
+ }),
237
+ })
238
+ expect(res1.status).toBe(200)
239
+
240
+ // Second join with same invitation should also succeed
241
+ const res2 = await app.request('/web/auth/join', {
242
+ method: 'POST',
243
+ headers: { 'Content-Type': 'application/json' },
244
+ body: JSON.stringify({
245
+ inviteToken,
246
+ username: 'user_two',
247
+ password: 'user-two-password-123',
248
+ }),
249
+ })
250
+ expect(res2.status).toBe(200)
251
+
252
+ // Third join should succeed (uses 3 of 3)
253
+ const res3 = await app.request('/web/auth/join', {
254
+ method: 'POST',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: JSON.stringify({
257
+ inviteToken,
258
+ username: 'user_three',
259
+ password: 'user-three-password-123',
260
+ }),
261
+ })
262
+ expect(res3.status).toBe(200)
263
+
264
+ // Fourth join should fail (max_uses reached)
265
+ const res4 = await app.request('/web/auth/join', {
266
+ method: 'POST',
267
+ headers: { 'Content-Type': 'application/json' },
268
+ body: JSON.stringify({
269
+ inviteToken,
270
+ username: 'user_four',
271
+ password: 'user-four-password-123',
272
+ }),
273
+ })
274
+ expect(res4.status).toBe(400)
275
+
276
+ // Verify 4 users exist: admin + 3 joined
277
+ const db = getDb!()
278
+ const count = db
279
+ .query('SELECT COUNT(*) as count FROM users')
280
+ .get() as { count: number }
281
+ expect(count.count).toBe(4)
282
+
283
+ // Verify invitation uses count
284
+ const tokenHash = createHash('sha256')
285
+ .update(inviteToken)
286
+ .digest('hex')
287
+ const inv = db
288
+ .query('SELECT uses, max_uses, consumed_at FROM invitations WHERE token_hash = $hash')
289
+ .get({ $hash: tokenHash }) as {
290
+ uses: number
291
+ max_uses: number
292
+ consumed_at: string | null
293
+ }
294
+ expect(inv.uses).toBe(3)
295
+ expect(inv.max_uses).toBe(3)
296
+ // consumed_at should be set after max_uses reached
297
+ expect(inv.consumed_at).not.toBeNull()
298
+ },
299
+ { timeout: 60000 },
300
+ )
301
+ })
302
+
303
+ // ===========================================================================
304
+ // Task 4: sessionAuth isAdmin from DB role
305
+ // ===========================================================================
306
+ describe('Security: sessionAuth isAdmin from DB role', () => {
307
+ let app: Hono
308
+ let adminAccessToken: string
309
+
310
+ beforeEach(async () => {
311
+ freshDb()
312
+ if (resetRateLimits) resetRateLimits()
313
+ app = createApp()
314
+ // Setup admin
315
+ await app.request('/web/auth/setup', {
316
+ method: 'POST',
317
+ headers: { 'Content-Type': 'application/json' },
318
+ body: JSON.stringify({
319
+ apiKey: 'test-api-key',
320
+ username: 'admin',
321
+ password: 'admin-password-123',
322
+ }),
323
+ })
324
+ // Login as admin
325
+ const loginRes = await app.request('/web/auth/login', {
326
+ method: 'POST',
327
+ headers: { 'Content-Type': 'application/json' },
328
+ body: JSON.stringify({
329
+ username: 'admin',
330
+ password: 'admin-password-123',
331
+ }),
332
+ })
333
+ const loginBody = await loginRes.json()
334
+ adminAccessToken = loginBody.accessToken || loginBody.access_token || ''
335
+ })
336
+
337
+ test('cookie-based admin login sets isAdmin=true', async () => {
338
+ // Create a protected route that exposes isAdmin
339
+ const { sessionAuth } = await import('../auth/middleware')
340
+ const testApp = new Hono()
341
+ testApp.get('/protected', async (c, next) => {
342
+ const result = await sessionAuth(c, next)
343
+ if (result) return result
344
+ return c.json({
345
+ userId: c.get('userId'),
346
+ isAdmin: c.get('isAdmin'),
347
+ })
348
+ })
349
+
350
+ // Use Bearer token auth (simulates cookie-based resolution via DB)
351
+ const res = await testApp.request('/protected', {
352
+ headers: { Authorization: `Bearer ${adminAccessToken}` },
353
+ })
354
+ expect(res.status).toBe(200)
355
+ const body = await res.json()
356
+ expect(body.isAdmin).toBe(true)
357
+ })
358
+ })
359
+
360
+ // ===========================================================================
361
+ // Task 5: /join username UNIQUE race returns 409 not 500
362
+ // ===========================================================================
363
+ describe('Security: /join username race returns 409', () => {
364
+ let app: Hono
365
+ let inviteToken1: string
366
+ let inviteToken2: string
367
+
368
+ beforeEach(async () => {
369
+ freshDb()
370
+ if (resetRateLimits) resetRateLimits()
371
+ app = createApp()
372
+ // Setup admin
373
+ await app.request('/web/auth/setup', {
374
+ method: 'POST',
375
+ headers: { 'Content-Type': 'application/json' },
376
+ body: JSON.stringify({
377
+ apiKey: 'test-api-key',
378
+ username: 'admin',
379
+ password: 'admin-password-123',
380
+ }),
381
+ })
382
+
383
+ // Create two separate max_uses=1 invitations so two joins can proceed
384
+ // simultaneously — the only conflict will be username uniqueness
385
+ const db = getDb!()
386
+ const futureExpiry = new Date(Date.now() + 86400000).toISOString()
387
+ const adminRow = db
388
+ .query("SELECT id FROM users WHERE username = 'admin'")
389
+ .get() as { id: string }
390
+
391
+ inviteToken1 = `inv_race1_${randomUUID().slice(0, 8)}`
392
+ const hash1 = createHash('sha256').update(inviteToken1).digest('hex')
393
+ db.exec(
394
+ `INSERT INTO invitations (token_hash, role, expires_at, max_uses, uses, created_by)
395
+ VALUES ('${hash1}', 'member', '${futureExpiry}', 1, 0, '${adminRow.id}')`,
396
+ )
397
+
398
+ inviteToken2 = `inv_race2_${randomUUID().slice(0, 8)}`
399
+ const hash2 = createHash('sha256').update(inviteToken2).digest('hex')
400
+ db.exec(
401
+ `INSERT INTO invitations (token_hash, role, expires_at, max_uses, uses, created_by)
402
+ VALUES ('${hash2}', 'member', '${futureExpiry}', 1, 0, '${adminRow.id}')`,
403
+ )
404
+
405
+ // Pre-insert a user with the conflicting username
406
+ db.exec(
407
+ `INSERT INTO users (id, username, password_hash, role, created_at)
408
+ VALUES ('usr_existing', 'duplicate_user', 'hash', 'member', '2026-01-01T00:00:00Z')`,
409
+ )
410
+ })
411
+
412
+ test('join with already-taken username returns 409 not 500', async () => {
413
+ const res = await app.request('/web/auth/join', {
414
+ method: 'POST',
415
+ headers: { 'Content-Type': 'application/json' },
416
+ body: JSON.stringify({
417
+ inviteToken: inviteToken1,
418
+ username: 'duplicate_user',
419
+ password: 'some-password-123',
420
+ }),
421
+ })
422
+ // Should return 409 Conflict, not 500
423
+ expect(res.status).toBe(409)
424
+ const body = await res.json()
425
+ expect(body.error).toMatch(/[Uu]sername/)
426
+ })
427
+ })
428
+
429
+ // ===========================================================================
430
+ // Task 8: CSRF middleware
431
+ // ===========================================================================
432
+ describe('Security: CSRF middleware', () => {
433
+ test('allows GET requests without Origin', async () => {
434
+ const { csrfCheck } = await import('../auth/csrf')
435
+ const app = new Hono()
436
+ app.use('/web/*', csrfCheck)
437
+ app.get('/web/ping', c => c.text('ok'))
438
+
439
+ const res = await app.request('/web/ping')
440
+ expect(res.status).toBe(200)
441
+ })
442
+
443
+ test('allows POST with matching Origin', async () => {
444
+ const { csrfCheck } = await import('../auth/csrf')
445
+ const app = new Hono()
446
+ app.use('/web/*', csrfCheck)
447
+ app.post('/web/action', c => c.json({ ok: true }))
448
+
449
+ const res = await app.request('http://localhost:3000/web/action', {
450
+ method: 'POST',
451
+ headers: {
452
+ Origin: 'http://localhost:3000',
453
+ 'Content-Type': 'application/json',
454
+ },
455
+ })
456
+ expect(res.status).toBe(200)
457
+ })
458
+
459
+ test('rejects POST with mismatched Origin', async () => {
460
+ const { csrfCheck } = await import('../auth/csrf')
461
+ const app = new Hono()
462
+ app.use('/web/*', csrfCheck)
463
+ app.post('/web/action', c => c.json({ ok: true }))
464
+
465
+ const res = await app.request('http://localhost:3000/web/action', {
466
+ method: 'POST',
467
+ headers: {
468
+ Origin: 'https://attacker.example',
469
+ 'Content-Type': 'application/json',
470
+ },
471
+ })
472
+ expect(res.status).toBe(403)
473
+ const body = await res.json()
474
+ expect(body.error).toContain('CSRF')
475
+ })
476
+
477
+ test('allows POST without Origin (non-browser client)', async () => {
478
+ const { csrfCheck } = await import('../auth/csrf')
479
+ const app = new Hono()
480
+ app.use('/web/*', csrfCheck)
481
+ app.post('/web/action', c => c.json({ ok: true }))
482
+
483
+ const res = await app.request('http://localhost:3000/web/action', {
484
+ method: 'POST',
485
+ headers: { 'Content-Type': 'application/json' },
486
+ })
487
+ expect(res.status).toBe(200)
488
+ })
489
+
490
+ test('allows DELETE with matching Origin', async () => {
491
+ const { csrfCheck } = await import('../auth/csrf')
492
+ const app = new Hono()
493
+ app.use('/web/*', csrfCheck)
494
+ app.delete('/web/resource/:id', c => c.json({ ok: true }))
495
+
496
+ const res = await app.request(
497
+ 'http://localhost:3000/web/resource/abc',
498
+ {
499
+ method: 'DELETE',
500
+ headers: {
501
+ Origin: 'http://localhost:3000',
502
+ },
503
+ },
504
+ )
505
+ expect(res.status).toBe(200)
506
+ })
507
+
508
+ test('rejects PUT with mismatched Origin', async () => {
509
+ const { csrfCheck } = await import('../auth/csrf')
510
+ const app = new Hono()
511
+ app.use('/web/*', csrfCheck)
512
+ app.put('/web/resource/:id', c => c.json({ ok: true }))
513
+
514
+ const res = await app.request(
515
+ 'http://localhost:3000/web/resource/abc',
516
+ {
517
+ method: 'PUT',
518
+ headers: {
519
+ Origin: 'https://evil.example',
520
+ 'Content-Type': 'application/json',
521
+ },
522
+ },
523
+ )
524
+ expect(res.status).toBe(403)
525
+ })
526
+ })