hfs 3.0.7 → 3.1.0-alpha2

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 (317) hide show
  1. package/README.md +1 -1
  2. package/admin/assets/index-CDiYkO8j.css +1 -0
  3. package/admin/assets/index-Dm5BN1iZ.js +1 -0
  4. package/admin/assets/index-P5i8LyWl.js +822 -0
  5. package/admin/assets/{sha512-gDcjPgJS.js → sha512-DG-ElrDe.js} +1 -1
  6. package/admin/flags/freakflags.css +284 -0
  7. package/admin/flags/sprite.png +0 -0
  8. package/admin/index.html +3 -2
  9. package/frontend/assets/index-legacy-CG_og8xL.js +1 -0
  10. package/frontend/assets/index-legacy-DDLKWGcm.js +9 -0
  11. package/frontend/assets/{sha512-legacy-BBwwadDl.js → sha512-legacy-D1zEZzDe.js} +1 -1
  12. package/frontend/fontello.css +3 -1
  13. package/frontend/fontello.woff2 +0 -0
  14. package/frontend/index.html +1 -1
  15. package/npm-shrinkwrap.json +16409 -0
  16. package/package.json +12 -11
  17. package/plugins/antibrute/plugin.js +14 -3
  18. package/src/AsapStream.js +49 -0
  19. package/src/ThrottledStream.js +1 -1
  20. package/src/adminApis.js +16 -5
  21. package/src/api.accounts.js +1 -6
  22. package/src/api.auth.js +103 -100
  23. package/src/api.get_file_list.js +7 -0
  24. package/src/api.lang.js +1 -0
  25. package/src/api.log.js +5 -3
  26. package/src/api.monitor.js +5 -0
  27. package/src/api.net.js +5 -0
  28. package/src/api.plugins.js +13 -2
  29. package/src/api.vfs.js +41 -28
  30. package/src/apiMiddleware.js +5 -4
  31. package/src/auth.js +39 -6
  32. package/src/commands.js +16 -27
  33. package/src/comments.js +4 -4
  34. package/src/config.js +5 -2
  35. package/src/const.js +2 -16
  36. package/src/cross-const.js +5 -2
  37. package/src/cross.js +21 -15
  38. package/src/expiringCache.js +16 -5
  39. package/src/fileAttr.js +1 -1
  40. package/src/first.js +7 -4
  41. package/src/frontEndApis.js +88 -102
  42. package/src/github.js +32 -21
  43. package/src/ips.js +1 -1
  44. package/src/log.js +5 -3
  45. package/src/middlewares.js +2 -1
  46. package/src/misc.js +2 -20
  47. package/src/perm.js +27 -12
  48. package/src/persistence.js +1 -1
  49. package/src/plugins.js +12 -6
  50. package/src/serveFile.js +3 -3
  51. package/src/serveGuiAndSharedFiles.js +3 -0
  52. package/src/serveGuiFiles.js +2 -1
  53. package/src/srp.js +5 -6
  54. package/src/stat.js +4 -1
  55. package/src/update.js +18 -25
  56. package/src/upload.js +4 -3
  57. package/src/util-files.js +2 -1
  58. package/src/util-http.js +3 -3
  59. package/src/vfs.js +4 -2
  60. package/src/webdav.js +297 -0
  61. package/admin/assets/index-Bj9Dk3JN.js +0 -822
  62. package/admin/assets/index-Byv1_NxP.css +0 -1
  63. package/admin/flags/ad.png +0 -0
  64. package/admin/flags/ae.png +0 -0
  65. package/admin/flags/af.png +0 -0
  66. package/admin/flags/ag.png +0 -0
  67. package/admin/flags/ai.png +0 -0
  68. package/admin/flags/al.png +0 -0
  69. package/admin/flags/am.png +0 -0
  70. package/admin/flags/ao.png +0 -0
  71. package/admin/flags/aq.png +0 -0
  72. package/admin/flags/ar.png +0 -0
  73. package/admin/flags/as.png +0 -0
  74. package/admin/flags/at.png +0 -0
  75. package/admin/flags/au.png +0 -0
  76. package/admin/flags/aw.png +0 -0
  77. package/admin/flags/ax.png +0 -0
  78. package/admin/flags/az.png +0 -0
  79. package/admin/flags/ba.png +0 -0
  80. package/admin/flags/bb.png +0 -0
  81. package/admin/flags/bd.png +0 -0
  82. package/admin/flags/be.png +0 -0
  83. package/admin/flags/bf.png +0 -0
  84. package/admin/flags/bg.png +0 -0
  85. package/admin/flags/bh.png +0 -0
  86. package/admin/flags/bi.png +0 -0
  87. package/admin/flags/bj.png +0 -0
  88. package/admin/flags/bl.png +0 -0
  89. package/admin/flags/bm.png +0 -0
  90. package/admin/flags/bn.png +0 -0
  91. package/admin/flags/bo.png +0 -0
  92. package/admin/flags/bq.png +0 -0
  93. package/admin/flags/br.png +0 -0
  94. package/admin/flags/bs.png +0 -0
  95. package/admin/flags/bt.png +0 -0
  96. package/admin/flags/bv.png +0 -0
  97. package/admin/flags/bw.png +0 -0
  98. package/admin/flags/by.png +0 -0
  99. package/admin/flags/bz.png +0 -0
  100. package/admin/flags/ca.png +0 -0
  101. package/admin/flags/cc.png +0 -0
  102. package/admin/flags/cd.png +0 -0
  103. package/admin/flags/cf.png +0 -0
  104. package/admin/flags/cg.png +0 -0
  105. package/admin/flags/ch.png +0 -0
  106. package/admin/flags/ci.png +0 -0
  107. package/admin/flags/ck.png +0 -0
  108. package/admin/flags/cl.png +0 -0
  109. package/admin/flags/cm.png +0 -0
  110. package/admin/flags/cn.png +0 -0
  111. package/admin/flags/co.png +0 -0
  112. package/admin/flags/cr.png +0 -0
  113. package/admin/flags/cu.png +0 -0
  114. package/admin/flags/cv.png +0 -0
  115. package/admin/flags/cw.png +0 -0
  116. package/admin/flags/cx.png +0 -0
  117. package/admin/flags/cy.png +0 -0
  118. package/admin/flags/cz.png +0 -0
  119. package/admin/flags/de.png +0 -0
  120. package/admin/flags/dj.png +0 -0
  121. package/admin/flags/dk.png +0 -0
  122. package/admin/flags/dm.png +0 -0
  123. package/admin/flags/do.png +0 -0
  124. package/admin/flags/dz.png +0 -0
  125. package/admin/flags/ec.png +0 -0
  126. package/admin/flags/ee.png +0 -0
  127. package/admin/flags/eg.png +0 -0
  128. package/admin/flags/eh.png +0 -0
  129. package/admin/flags/er.png +0 -0
  130. package/admin/flags/es.png +0 -0
  131. package/admin/flags/et.png +0 -0
  132. package/admin/flags/fi.png +0 -0
  133. package/admin/flags/fj.png +0 -0
  134. package/admin/flags/fk.png +0 -0
  135. package/admin/flags/fm.png +0 -0
  136. package/admin/flags/fo.png +0 -0
  137. package/admin/flags/fr.png +0 -0
  138. package/admin/flags/ga.png +0 -0
  139. package/admin/flags/gb-eng.png +0 -0
  140. package/admin/flags/gb-nir.png +0 -0
  141. package/admin/flags/gb-sct.png +0 -0
  142. package/admin/flags/gb-wls.png +0 -0
  143. package/admin/flags/gb.png +0 -0
  144. package/admin/flags/gd.png +0 -0
  145. package/admin/flags/ge.png +0 -0
  146. package/admin/flags/gf.png +0 -0
  147. package/admin/flags/gg.png +0 -0
  148. package/admin/flags/gh.png +0 -0
  149. package/admin/flags/gi.png +0 -0
  150. package/admin/flags/gl.png +0 -0
  151. package/admin/flags/gm.png +0 -0
  152. package/admin/flags/gn.png +0 -0
  153. package/admin/flags/gp.png +0 -0
  154. package/admin/flags/gq.png +0 -0
  155. package/admin/flags/gr.png +0 -0
  156. package/admin/flags/gs.png +0 -0
  157. package/admin/flags/gt.png +0 -0
  158. package/admin/flags/gu.png +0 -0
  159. package/admin/flags/gw.png +0 -0
  160. package/admin/flags/gy.png +0 -0
  161. package/admin/flags/hk.png +0 -0
  162. package/admin/flags/hm.png +0 -0
  163. package/admin/flags/hn.png +0 -0
  164. package/admin/flags/hr.png +0 -0
  165. package/admin/flags/ht.png +0 -0
  166. package/admin/flags/hu.png +0 -0
  167. package/admin/flags/id.png +0 -0
  168. package/admin/flags/ie.png +0 -0
  169. package/admin/flags/il.png +0 -0
  170. package/admin/flags/im.png +0 -0
  171. package/admin/flags/in.png +0 -0
  172. package/admin/flags/io.png +0 -0
  173. package/admin/flags/iq.png +0 -0
  174. package/admin/flags/ir.png +0 -0
  175. package/admin/flags/is.png +0 -0
  176. package/admin/flags/it.png +0 -0
  177. package/admin/flags/je.png +0 -0
  178. package/admin/flags/jm.png +0 -0
  179. package/admin/flags/jo.png +0 -0
  180. package/admin/flags/jp.png +0 -0
  181. package/admin/flags/ke.png +0 -0
  182. package/admin/flags/kg.png +0 -0
  183. package/admin/flags/kh.png +0 -0
  184. package/admin/flags/ki.png +0 -0
  185. package/admin/flags/km.png +0 -0
  186. package/admin/flags/kn.png +0 -0
  187. package/admin/flags/kp.png +0 -0
  188. package/admin/flags/kr.png +0 -0
  189. package/admin/flags/kw.png +0 -0
  190. package/admin/flags/ky.png +0 -0
  191. package/admin/flags/kz.png +0 -0
  192. package/admin/flags/la.png +0 -0
  193. package/admin/flags/lb.png +0 -0
  194. package/admin/flags/lc.png +0 -0
  195. package/admin/flags/li.png +0 -0
  196. package/admin/flags/lk.png +0 -0
  197. package/admin/flags/lr.png +0 -0
  198. package/admin/flags/ls.png +0 -0
  199. package/admin/flags/lt.png +0 -0
  200. package/admin/flags/lu.png +0 -0
  201. package/admin/flags/lv.png +0 -0
  202. package/admin/flags/ly.png +0 -0
  203. package/admin/flags/ma.png +0 -0
  204. package/admin/flags/mc.png +0 -0
  205. package/admin/flags/md.png +0 -0
  206. package/admin/flags/me.png +0 -0
  207. package/admin/flags/mf.png +0 -0
  208. package/admin/flags/mg.png +0 -0
  209. package/admin/flags/mh.png +0 -0
  210. package/admin/flags/mk.png +0 -0
  211. package/admin/flags/ml.png +0 -0
  212. package/admin/flags/mm.png +0 -0
  213. package/admin/flags/mn.png +0 -0
  214. package/admin/flags/mo.png +0 -0
  215. package/admin/flags/mp.png +0 -0
  216. package/admin/flags/mq.png +0 -0
  217. package/admin/flags/mr.png +0 -0
  218. package/admin/flags/ms.png +0 -0
  219. package/admin/flags/mt.png +0 -0
  220. package/admin/flags/mu.png +0 -0
  221. package/admin/flags/mv.png +0 -0
  222. package/admin/flags/mw.png +0 -0
  223. package/admin/flags/mx.png +0 -0
  224. package/admin/flags/my.png +0 -0
  225. package/admin/flags/mz.png +0 -0
  226. package/admin/flags/na.png +0 -0
  227. package/admin/flags/nc.png +0 -0
  228. package/admin/flags/ne.png +0 -0
  229. package/admin/flags/nf.png +0 -0
  230. package/admin/flags/ng.png +0 -0
  231. package/admin/flags/ni.png +0 -0
  232. package/admin/flags/nl.png +0 -0
  233. package/admin/flags/no.png +0 -0
  234. package/admin/flags/np.png +0 -0
  235. package/admin/flags/nr.png +0 -0
  236. package/admin/flags/nu.png +0 -0
  237. package/admin/flags/nz.png +0 -0
  238. package/admin/flags/om.png +0 -0
  239. package/admin/flags/pa.png +0 -0
  240. package/admin/flags/pe.png +0 -0
  241. package/admin/flags/pf.png +0 -0
  242. package/admin/flags/pg.png +0 -0
  243. package/admin/flags/ph.png +0 -0
  244. package/admin/flags/pk.png +0 -0
  245. package/admin/flags/pl.png +0 -0
  246. package/admin/flags/pm.png +0 -0
  247. package/admin/flags/pn.png +0 -0
  248. package/admin/flags/pr.png +0 -0
  249. package/admin/flags/ps.png +0 -0
  250. package/admin/flags/pt.png +0 -0
  251. package/admin/flags/pw.png +0 -0
  252. package/admin/flags/py.png +0 -0
  253. package/admin/flags/qa.png +0 -0
  254. package/admin/flags/re.png +0 -0
  255. package/admin/flags/ro.png +0 -0
  256. package/admin/flags/rs.png +0 -0
  257. package/admin/flags/ru.png +0 -0
  258. package/admin/flags/rw.png +0 -0
  259. package/admin/flags/sa.png +0 -0
  260. package/admin/flags/sb.png +0 -0
  261. package/admin/flags/sc.png +0 -0
  262. package/admin/flags/sd.png +0 -0
  263. package/admin/flags/se.png +0 -0
  264. package/admin/flags/sg.png +0 -0
  265. package/admin/flags/sh.png +0 -0
  266. package/admin/flags/si.png +0 -0
  267. package/admin/flags/sj.png +0 -0
  268. package/admin/flags/sk.png +0 -0
  269. package/admin/flags/sl.png +0 -0
  270. package/admin/flags/sm.png +0 -0
  271. package/admin/flags/sn.png +0 -0
  272. package/admin/flags/so.png +0 -0
  273. package/admin/flags/sr.png +0 -0
  274. package/admin/flags/ss.png +0 -0
  275. package/admin/flags/st.png +0 -0
  276. package/admin/flags/sv.png +0 -0
  277. package/admin/flags/sx.png +0 -0
  278. package/admin/flags/sy.png +0 -0
  279. package/admin/flags/sz.png +0 -0
  280. package/admin/flags/tc.png +0 -0
  281. package/admin/flags/td.png +0 -0
  282. package/admin/flags/tf.png +0 -0
  283. package/admin/flags/tg.png +0 -0
  284. package/admin/flags/th.png +0 -0
  285. package/admin/flags/tj.png +0 -0
  286. package/admin/flags/tk.png +0 -0
  287. package/admin/flags/tl.png +0 -0
  288. package/admin/flags/tm.png +0 -0
  289. package/admin/flags/tn.png +0 -0
  290. package/admin/flags/to.png +0 -0
  291. package/admin/flags/tr.png +0 -0
  292. package/admin/flags/tt.png +0 -0
  293. package/admin/flags/tv.png +0 -0
  294. package/admin/flags/tw.png +0 -0
  295. package/admin/flags/tz.png +0 -0
  296. package/admin/flags/ua.png +0 -0
  297. package/admin/flags/ug.png +0 -0
  298. package/admin/flags/um.png +0 -0
  299. package/admin/flags/us.png +0 -0
  300. package/admin/flags/uy.png +0 -0
  301. package/admin/flags/uz.png +0 -0
  302. package/admin/flags/va.png +0 -0
  303. package/admin/flags/vc.png +0 -0
  304. package/admin/flags/ve.png +0 -0
  305. package/admin/flags/vg.png +0 -0
  306. package/admin/flags/vi.png +0 -0
  307. package/admin/flags/vn.png +0 -0
  308. package/admin/flags/vu.png +0 -0
  309. package/admin/flags/wf.png +0 -0
  310. package/admin/flags/ws.png +0 -0
  311. package/admin/flags/xk.png +0 -0
  312. package/admin/flags/ye.png +0 -0
  313. package/admin/flags/yt.png +0 -0
  314. package/admin/flags/za.png +0 -0
  315. package/admin/flags/zm.png +0 -0
  316. package/admin/flags/zw.png +0 -0
  317. package/frontend/assets/index-legacy-COBT1YmS.js +0 -50
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hfs",
3
- "version": "3.0.7",
3
+ "version": "3.1.0-alpha2",
4
4
  "description": "HTTP File Server",
5
5
  "keywords": ["file server", "http server"],
6
6
  "homepage": "https://rejetto.com/hfs",
@@ -13,8 +13,8 @@
13
13
  "watch-server-full": "npm run start --workspace=frontend & npm run start --workspace=admin & cross-env FRONTEND_PROXY=3005 ADMIN_PROXY=3006 npm run watch-server",
14
14
  "start-frontend": "npm run start --workspace=frontend",
15
15
  "start-admin": "npm run start --workspace=admin",
16
- "build-all": "rm -rf dist && npm run build-server && npm run test-with-server && (npm run build-frontend & npm run build-admin) && echo COMPLETED",
17
- "build-server": "rm -rf dist/src dist/plugins && npm i && tsc && touch package.json && cp -v -r package.json central.json README* LICENSE* hfs.ico plugins dist && find dist -name .DS_Store -o -name storage -exec rm -rf {} + && node afterbuild.js",
16
+ "build-all": "rm -rf dist && npm run build-server && (npm run build-frontend & npm run build-admin) && echo COMPLETED",
17
+ "build-server": "rm -rf dist/src dist/plugins && npm i && tsc && touch package.json && cp -v -r package.json central.json README* LICENSE* hfs.ico plugins dist && find dist -name .DS_Store -o -name storage -exec rm -rf {} + && node scripts/afterbuild.js",
18
18
  "build-frontend": "npm run build --workspace=frontend",
19
19
  "build-admin": "npm run build --workspace=admin",
20
20
  "server-for-test": "node dist/src --cwd tests/work --config tests --debug",
@@ -22,13 +22,13 @@
22
22
  "test": "node --import tsx --test tests/test.ts",
23
23
  "test-with-server": "sh -c 'npm run port-is-free && tsc && rm -rf tests/work tests/tmp && (node dist/src --cwd tests/work --config tests & echo $! > .server_pid) && sleep 2 && node --import tsx --test \"$@\" tests/test.ts; _exit=$?; if [ -f ./.server_pid ]; then SERVER_PID=$(cat ./.server_pid); kill \"$SERVER_PID\" 2>/dev/null || true; rm -f ./.server_pid; fi; exit $_exit' --",
24
24
  "port-is-free": "node -e \"const port=process.argv[1]||8081;process.exit(await fetch('http://localhost:'+port).then(() => console.log('BUSY')||1, () => 0))\" --",
25
- "test-ui": "npx playwright test frontend && npx playwright test serial",
26
- "test-with-ui": "sh -c 'npm run port-is-free -- 3005 && npm run start-frontend & npm run port-is-free -- 3006 && npm run start-admin & cross-env TEST_WITH_UI=1 npx playwright test --ui'",
25
+ "test-ui": "npx playwright test frontend && npx playwright test serial && npx playwright test admin-vfs",
26
+ "test-with-ui": "sh -c 'npm run port-is-free -- 3005 && npm run start-frontend & npm run port-is-free -- 3006 && npm run start-admin & cross-env TEST_WITH_UI=1 npx playwright test --ui \"$@\"' --",
27
27
  "pub": "cd dist && npm publish",
28
28
  "dist": "STASHED=; if ! git diff-index --quiet HEAD --; then git stash push -m 'dist' && STASHED=1; fi; CI=1 FORCE_COLOR=1 npm run dist-uncommitted || (EXIT_CODE=$?; [ -n \"$STASHED\" ] && git stash pop; exit $EXIT_CODE); [ -n \"$STASHED\" ] && git stash pop",
29
- "dist-uncommitted": "npm audit --omit=dev --audit-level=moderate && npm run build-all && npm run test-ui && npm run dist-bin",
29
+ "dist-uncommitted": "npm audit --omit=dev --audit-level=moderate && rm -rf dist && npm run build-server && npm run test-with-server && (npm run build-frontend & npm run build-admin) && npm run test-ui && npm run dist-bin",
30
30
  "dist-bin": "npm run dist-modules && npm run dist-bin-win && npm run dist-bin-linux && npm run dist-bin-linux-arm && npm run dist-bin-mac && npm run dist-bin-mac-arm",
31
- "dist-modules": "cp package*.json central.json README.md dist && cd dist && npm ci --omit=dev && cd .. && node prune_modules",
31
+ "dist-modules": "cp package*.json central.json README.md dist && cd dist && npm ci --omit=dev && npm shrinkwrap && cd .. && node scripts/prune_modules.js",
32
32
  "dist-bin-win": "cd dist && pkg . --public -C gzip -t node20-win-x64 && npx resedit-cli --in hfs.exe --icon 1,../hfs.ico --out hfs.exe && zip hfs-windows-x64-$(jq -r .version ../package.json).zip hfs.exe -r plugins && cd ..",
33
33
  "dist-bin-mac-arm": "cd dist && pkg . --public -C gzip -t node20-macos-arm64 && zip hfs-mac-arm64-$(jq -r .version ../package.json).zip hfs -r plugins && cd ..",
34
34
  "dist-bin-mac": "cd dist && pkg . --public -C gzip -t node20-macos-x64 && zip hfs-mac-x64-$(jq -r .version ../package.json).zip hfs -r plugins && cd ..",
@@ -45,6 +45,7 @@
45
45
  },
46
46
  "files": [
47
47
  "central.json",
48
+ "npm-shrinkwrap.json",
48
49
  "src/*",
49
50
  "admin/*",
50
51
  "frontend/*",
@@ -79,7 +80,7 @@
79
80
  },
80
81
  "dependencies": {
81
82
  "@gregoranders/csv": "^0.0.13",
82
- "@rejetto/kvstorage": "^0.16.0",
83
+ "@rejetto/kvstorage": "^0.17.1",
83
84
  "acme-client": "^5.4.0",
84
85
  "busboy": "^1.6.0",
85
86
  "crc-32": "^1.2.2",
@@ -90,8 +91,8 @@
90
91
  "iconv-lite": "^0.7.0",
91
92
  "immer": "^10.1.3",
92
93
  "ip2location-nodejs": "^9.7.0",
93
- "koa": "^3.1.1",
94
- "koa-compress": "^5.1.1",
94
+ "koa": "^3.1.2",
95
+ "koa-compress": "^5.2.0",
95
96
  "koa-mount": "^4.2.0",
96
97
  "koa-session": "^7.0.2",
97
98
  "limiter": "^3.0.0",
@@ -122,7 +123,7 @@
122
123
  "@types/node": "^20.17.30",
123
124
  "@types/node-forge": "^1.3.14",
124
125
  "@types/picomatch": "^4.0.2",
125
- "@yao-pkg/pkg": "6.13.1",
126
+ "@yao-pkg/pkg": "6.14.1",
126
127
  "cross-env": "^10.0.0",
127
128
  "koa-better-http-proxy": "^0.2.10",
128
129
  "nm-prune": "^5.0.0",
@@ -1,4 +1,4 @@
1
- exports.version = 3
1
+ exports.version = 3.1
2
2
  exports.description = "Introduce increasing delays between login attempts."
3
3
  exports.apiRequired = 9.6 // addBlock
4
4
 
@@ -7,6 +7,7 @@ exports.config = {
7
7
  max: { type: 'number', min: 1, defaultValue: 60, label: "Max delay", unit: "seconds", helperText: "Max seconds to delay before next login is allowed" },
8
8
  blockAfter: { type: 'number', xs: 6, min: 1, max: 9999, defaultValue: 100, label: "Block IP after", unit: "attempts", helperText: "localhost excluded" },
9
9
  blockForHours: { type: 'number', xs: 6, min: 0, defaultValue: 24, label: "Block for", unit: "hours" },
10
+ exclude: { type: 'string', defaultValue: '', label: "Exclude IPs", helperText: "Net mask syntax" },
10
11
  }
11
12
  exports.configDialog = {
12
13
  maxWidth: 'xs',
@@ -15,7 +16,7 @@ exports.configDialog = {
15
16
  const byIp = {}
16
17
 
17
18
  exports.init = api => {
18
- const { isLocalHost, HOUR } = api.misc
19
+ const { isLocalHost, HOUR, netMatches } = api.misc
19
20
  api.events.multi({
20
21
  async attemptingLogin({ ctx }) {
21
22
  const { ip } = ctx
@@ -25,7 +26,7 @@ exports.init = api => {
25
26
  const delay = Math.min(max, 1000 * api.getConfig('increment') * ++rec.attempts)
26
27
  const wait = rec.next - now
27
28
  rec.next = new Date(+rec.next + delay)
28
- if (rec.attempts > api.getConfig('blockAfter') && !isLocalHost(ctx)) {
29
+ if (rec.attempts > api.getConfig('blockAfter') && !isLocalHost(ctx) && !isExcluded(ip)) {
29
30
  const hours = api.getConfig('blockForHours')
30
31
  api.addBlock({ ip, comment: "From antibrute plugin", expire: hours ? new Date(now.getTime() + hours * HOUR) : undefined })
31
32
  }
@@ -42,4 +43,14 @@ exports.init = api => {
42
43
  delete byIp[ctx.ip] // reset if login was successful
43
44
  }
44
45
  })
46
+
47
+ function isExcluded(ip) {
48
+ const mask = api.getConfig('exclude')
49
+ if (!mask) return false
50
+ try { return netMatches(ip, mask) }
51
+ catch (e) {
52
+ api.log("bad exclude mask:", String(e))
53
+ return false
54
+ }
55
+ }
45
56
  }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AsapStream = void 0;
4
+ const stream_1 = require("stream");
5
+ const cross_1 = require("./cross");
6
+ // produces as promises resolve, not sequentially
7
+ class AsapStream extends stream_1.Readable {
8
+ promises;
9
+ finished = false;
10
+ constructor(promises) {
11
+ super({ objectMode: true });
12
+ this.promises = promises;
13
+ }
14
+ _read() {
15
+ if (this.finished)
16
+ return;
17
+ this.finished = true;
18
+ void (async () => {
19
+ const pending = [];
20
+ try {
21
+ if ((0, cross_1.isAsyncIterable)(this.promises)) {
22
+ const iterator = this.promises[Symbol.asyncIterator]();
23
+ while (true) {
24
+ const { value, done } = await iterator.next();
25
+ if (done)
26
+ break;
27
+ const promise = Promise.resolve(value);
28
+ pending.push(promise);
29
+ promise.then(x => x !== undefined && this.push(x), e => this.emit('error', e));
30
+ }
31
+ }
32
+ else {
33
+ for (const p of this.promises) {
34
+ const promise = Promise.resolve(p);
35
+ pending.push(promise);
36
+ promise.then(x => x !== undefined && this.push(x), e => this.emit('error', e));
37
+ }
38
+ }
39
+ await Promise.allSettled(pending);
40
+ this.push(null);
41
+ }
42
+ catch (e) {
43
+ this.emit('error', e);
44
+ this.push(null);
45
+ }
46
+ })();
47
+ }
48
+ }
49
+ exports.AsapStream = AsapStream;
@@ -72,7 +72,7 @@ class ThrottleGroup {
72
72
  }
73
73
  updateLimit(kBs) {
74
74
  if (kBs < 0)
75
- throw new Error('invalid bytesPerSecond');
75
+ throw Error('invalid bytesPerSecond');
76
76
  kBs *= 1000;
77
77
  return this.bucket = new limiter_1.TokenBucket({
78
78
  bucketSize: kBs,
package/src/adminApis.js CHANGED
@@ -71,10 +71,16 @@ exports.adminApis = {
71
71
  customHtml: customHtml_1.customHtml.getText(),
72
72
  };
73
73
  },
74
- set_config_text: ({ text }) => config_1.configFile.save(text, { reparse: true }),
75
- update: ({ tag }) => (0, update_1.update)(tag).catch(e => {
76
- throw e.cause?.statusCode ? new apiMiddleware_1.ApiError(e.cause?.statusCode) : e;
77
- }),
74
+ set_config_text: ({ text }) => {
75
+ (0, misc_1.apiAssertTypes)({ string: { text } });
76
+ return config_1.configFile.save(text, { reparse: true });
77
+ },
78
+ update: ({ tag }) => {
79
+ (0, misc_1.apiAssertTypes)({ string_undefined: { tag } });
80
+ return (0, update_1.update)(tag).catch(e => {
81
+ throw e.cause?.statusCode ? new apiMiddleware_1.ApiError(e.cause?.statusCode) : e;
82
+ });
83
+ },
78
84
  async check_update() {
79
85
  return { options: await (0, update_1.getUpdates)() };
80
86
  },
@@ -88,6 +94,10 @@ exports.adminApis = {
88
94
  return {};
89
95
  },
90
96
  async ip_country({ ips }) {
97
+ (0, misc_1.apiAssertTypes)({
98
+ array: { ips },
99
+ string: { ips0: ips[0] }
100
+ });
91
101
  const res = await Promise.allSettled(ips.map(geo_1.ip2country));
92
102
  return {
93
103
  codes: res.map(x => x.status === 'rejected' || x.value === '-' ? '' : x.value)
@@ -110,6 +120,7 @@ exports.adminApis = {
110
120
  };
111
121
  },
112
122
  async set_custom_html({ sections }) {
123
+ (0, misc_1.apiAssertTypes)({ object: { sections } });
113
124
  await (0, customHtml_1.saveCustomHtml)(sections);
114
125
  return {};
115
126
  },
@@ -196,7 +207,7 @@ const frpDebounced = (0, misc_1.debounceAsync)(async () => {
196
207
  }
197
208
  }, { retain: 10_000 });
198
209
  function anyAccountCanLoginAdmin() {
199
- return Boolean(lodash_1.default.find(perm_1.accounts.get(), perm_1.accountCanLoginAdmin));
210
+ return lodash_1.default.some(perm_1.accounts.get(), perm_1.accountCanLoginAdmin);
200
211
  }
201
212
  function preventAdminAccess(ctx) {
202
213
  return !(0, misc_1.isLocalHost)(ctx) && !exports.adminNet.compiled()(ctx.ip);
@@ -40,6 +40,7 @@ exports.default = {
40
40
  return { list: Object.keys(perm_1.accounts.get()) };
41
41
  },
42
42
  get_account({ username }, ctx) {
43
+ (0, misc_1.apiAssertTypes)({ string: { username } });
43
44
  return prepareAccount((0, perm_1.getAccount)(username || (0, auth_1.getCurrentUsername)(ctx)))
44
45
  || new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
45
46
  },
@@ -85,10 +86,4 @@ exports.default = {
85
86
  auth_1.invalidateSessionBefore.set((0, perm_1.normalizeUsername)(username), Date.now());
86
87
  return {};
87
88
  },
88
- async change_srp({ username, salt, verifier }) {
89
- (0, misc_1.apiAssertTypes)({ string: { username, salt, verifier } });
90
- const a = (0, perm_1.getAccount)(username);
91
- return a ? (0, perm_1.changeSrpHelper)(a, salt, verifier)
92
- : new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
93
- }
94
89
  };
package/src/api.auth.js CHANGED
@@ -4,7 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.change_my_srp = exports.refresh_session = exports.logout = exports.loginSrp2 = exports.loginSrp1 = exports.login = void 0;
7
+ exports.authApis = void 0;
8
8
  const perm_1 = require("./perm");
9
9
  const apiMiddleware_1 = require("./apiMiddleware");
10
10
  const const_1 = require("./const");
@@ -13,103 +13,14 @@ const middlewares_1 = require("./middlewares");
13
13
  const auth_1 = require("./auth");
14
14
  const config_1 = require("./config");
15
15
  const events_1 = __importDefault(require("./events"));
16
+ const misc_1 = require("./misc");
16
17
  const ongoingLogins = {}; // store data that doesn't fit session object
17
18
  const keepSessionAlive = (0, config_1.defineConfig)('keep_session_alive', true);
18
- const login = async ({ username, password }, ctx) => {
19
- if (!username)
20
- return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
21
- if (!ctx.session)
22
- return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
23
- try {
24
- const account = await (0, auth_1.clearTextLogin)(ctx, username, password, 'api');
25
- if (!account)
26
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
27
- await (0, auth_1.setLoggedIn)(ctx, account.username);
28
- }
29
- catch (e) {
30
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, String(e));
31
- }
32
- return {
33
- redirect: ctx.state.account?.redirect,
34
- ...await (0, exports.refresh_session)({}, ctx)
35
- };
36
- };
37
- exports.login = login;
38
- const loginSrp1 = async ({ username }, ctx) => {
39
- if (!username)
40
- return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
41
- const account = (0, perm_1.getAccount)(username);
42
- if (!ctx.session)
43
- return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
44
- if (account?.plugin?.auth) // tell client to do clear-text login, before firing attemptingLogin, before triggering anti-brute
45
- return new apiMiddleware_1.ApiError(const_1.HTTP_METHOD_NOT_ALLOWED);
46
- if ((await events_1.default.emitAsync('attemptingLogin', { ctx, username }))?.isDefaultPrevented())
47
- return;
48
- if (!account || !(0, perm_1.accountCanLogin)(account)) { // TODO simulate fake account to prevent knowing valid usernames
49
- ctx.logExtra({ u: username });
50
- ctx.state.dontLog = false; // log even if log_api is false
51
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, account && (0, perm_1.accountIsDisabled)(account) ? 'Account disabled' : undefined);
52
- }
53
- if ((0, middlewares_1.failAllowNet)(ctx, account))
54
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
55
- try {
56
- const { srpServer, ...rest } = await (0, auth_1.srpServerStep1)(account);
57
- const sid = Math.random();
58
- ongoingLogins[sid] = srpServer;
59
- setTimeout(() => delete ongoingLogins[sid], 60_000);
60
- ctx.session.loggingIn = { username, sid }; // temporarily store until process is complete
61
- return rest;
62
- }
63
- catch (code) {
64
- return new apiMiddleware_1.ApiError(code);
65
- }
66
- };
67
- exports.loginSrp1 = loginSrp1;
68
- const loginSrp2 = async ({ pubKey, proof }, ctx) => {
69
- if (!ctx.session)
70
- return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
71
- if (!ctx.session.loggingIn)
72
- return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT);
73
- const { username, sid } = ctx.session.loggingIn;
74
- delete ctx.session.loggingIn;
75
- const step1 = ongoingLogins[sid];
76
- if (!step1)
77
- return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
78
- try {
79
- const M2 = await step1.step2(BigInt(pubKey), BigInt(proof))
80
- .catch(() => { throw ''; });
81
- await (0, auth_1.setLoggedIn)(ctx, username);
82
- return {
83
- proof: String(M2),
84
- redirect: ctx.state.account?.redirect,
85
- ...await (0, exports.refresh_session)({}, ctx)
86
- };
87
- }
88
- catch (e) {
89
- ctx.logExtra({ u: username });
90
- ctx.state.dontLog = false; // log even if log_api is false
91
- events_1.default.emit('failedLogin', { ctx, username });
92
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, e ? String(e) : undefined);
93
- }
94
- finally {
95
- delete ongoingLogins[sid];
96
- }
97
- };
98
- exports.loginSrp2 = loginSrp2;
99
- // this api is here for consistency, but frontend is actually using
100
- const logout = async ({}, ctx) => {
101
- if (!ctx.session)
102
- return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
103
- await (0, auth_1.setLoggedIn)(ctx, false);
104
- // 401 is a convenient code for OK: the browser clears a possible http authentication (hopefully), and Admin automatically triggers login dialog
105
- return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
106
- };
107
- exports.logout = logout;
108
19
  const refresh_session = async ({}, ctx) => {
109
20
  const username = (0, auth_1.getCurrentUsername)(ctx);
110
21
  return !ctx.session ? new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR) : {
111
22
  username,
112
- expandedUsername: (0, perm_1.expandUsername)(username),
23
+ expandedUsername: Array.from((0, perm_1.expandUsername)(username)),
113
24
  adminUrl: (0, adminApis_1.ctxAdminAccess)(ctx) ? ctx.state.revProxyPath + const_1.ADMIN_URI : undefined,
114
25
  canChangePassword: (0, perm_1.accountCanChangePassword)(ctx.state.account),
115
26
  requireChangePassword: ctx.state.account?.require_password_change,
@@ -117,12 +28,104 @@ const refresh_session = async ({}, ctx) => {
117
28
  accountExp: ctx.state.account?.expire,
118
29
  };
119
30
  };
120
- exports.refresh_session = refresh_session;
121
- const change_my_srp = async ({ salt, verifier }, ctx) => {
122
- const a = ctx.state.account;
123
- return !a || !(0, perm_1.accountCanChangePassword)(a) ? new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED)
124
- : (0, perm_1.changeSrpHelper)(a, salt, verifier).then(() => {
125
- delete a.require_password_change;
126
- });
31
+ exports.authApis = {
32
+ async login({ username, password }, ctx) {
33
+ if (!username)
34
+ return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
35
+ if (!ctx.session)
36
+ return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
37
+ try {
38
+ const account = await (0, auth_1.clearTextLogin)(ctx, username, password, 'api');
39
+ if (!account)
40
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
41
+ await (0, auth_1.setLoggedIn)(ctx, account.username);
42
+ }
43
+ catch (e) {
44
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, String(e));
45
+ }
46
+ return {
47
+ redirect: ctx.state.account?.redirect,
48
+ ...await refresh_session({}, ctx)
49
+ };
50
+ },
51
+ async loginSrp1({ username }, ctx) {
52
+ (0, misc_1.apiAssertTypes)({ string: { username } });
53
+ if (!username)
54
+ return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST);
55
+ const account = (0, perm_1.getAccount)(username);
56
+ if (!ctx.session)
57
+ return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
58
+ if (account?.plugin?.auth) // tell client to do clear-text login, before firing attemptingLogin, before triggering anti-brute
59
+ return new apiMiddleware_1.ApiError(const_1.HTTP_METHOD_NOT_ALLOWED);
60
+ if ((await events_1.default.emitAsync('attemptingLogin', { ctx, username }))?.isDefaultPrevented())
61
+ return;
62
+ if (!account || !(0, perm_1.accountCanLogin)(account)) { // TODO simulate fake account to prevent knowing valid usernames
63
+ ctx.logExtra({ u: username });
64
+ ctx.state.dontLog = false; // log even if log_api is false
65
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, account && (0, perm_1.accountIsDisabled)(account) ? 'Account disabled' : undefined);
66
+ }
67
+ if ((0, middlewares_1.failAllowNet)(ctx, account))
68
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
69
+ try {
70
+ const { srpServer, ...rest } = await (0, auth_1.srpServerStep1)(account);
71
+ const sid = Math.random();
72
+ ongoingLogins[sid] = srpServer;
73
+ setTimeout(() => delete ongoingLogins[sid], 60_000);
74
+ ctx.session.loggingIn = { username, sid }; // temporarily store until process is complete
75
+ return rest;
76
+ }
77
+ catch (code) {
78
+ return new apiMiddleware_1.ApiError(code);
79
+ }
80
+ },
81
+ async loginSrp2({ pubKey, proof }, ctx) {
82
+ if (!ctx.session)
83
+ return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
84
+ if (!ctx.session.loggingIn)
85
+ return new apiMiddleware_1.ApiError(const_1.HTTP_CONFLICT);
86
+ const { username, sid } = ctx.session.loggingIn;
87
+ delete ctx.session.loggingIn;
88
+ const step1 = ongoingLogins[sid];
89
+ if (!step1)
90
+ return new apiMiddleware_1.ApiError(const_1.HTTP_NOT_FOUND);
91
+ try {
92
+ const M2 = await step1.step2(BigInt(pubKey), BigInt(proof))
93
+ .catch(() => { throw ''; });
94
+ await (0, auth_1.setLoggedIn)(ctx, username);
95
+ return {
96
+ proof: String(M2),
97
+ redirect: ctx.state.account?.redirect,
98
+ ...await refresh_session({}, ctx)
99
+ };
100
+ }
101
+ catch (e) {
102
+ ctx.logExtra({ u: username });
103
+ ctx.state.dontLog = false; // log even if log_api is false
104
+ events_1.default.emit('failedLogin', { ctx, username });
105
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED, e ? String(e) : undefined);
106
+ }
107
+ finally {
108
+ delete ongoingLogins[sid];
109
+ }
110
+ },
111
+ // this api is here for consistency, but frontend is actually using
112
+ async logout({}, ctx) {
113
+ if (!ctx.session)
114
+ return new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR);
115
+ await (0, auth_1.setLoggedIn)(ctx, false);
116
+ // 401 is a convenient code for OK: the browser clears a possible http authentication (hopefully), and Admin automatically triggers login dialog
117
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
118
+ },
119
+ refresh_session,
120
+ async change_srp({ username, salt, verifier }, ctx) {
121
+ const a = username && (0, perm_1.getAccount)(username);
122
+ const can = a && ((0, adminApis_1.ctxAdminAccess)(ctx) || username === (0, auth_1.getCurrentUsername)(ctx) && (0, perm_1.accountCanChangePassword)(a));
123
+ if (!can)
124
+ return new apiMiddleware_1.ApiError(const_1.HTTP_UNAUTHORIZED);
125
+ if (!salt || !verifier)
126
+ return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'missing parameters');
127
+ await (0, perm_1.updateAccount)(a, a => (0, perm_1.saveSrpInfo)(a, salt, verifier));
128
+ delete a.require_password_change;
129
+ return {};
130
+ }
127
131
  };
128
- exports.change_my_srp = change_my_srp;
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
2
  // This file is part of HFS - Copyright 2021-2023, Massimo Melina <a@rejetto.com> - License https://www.gnu.org/licenses/gpl-3.0.txt
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
3
6
  Object.defineProperty(exports, "__esModule", { value: true });
4
7
  exports.get_file_list = void 0;
5
8
  exports.paramsToFilter = paramsToFilter;
@@ -14,6 +17,7 @@ const connections_1 = require("./connections");
14
17
  const adminApis_1 = require("./adminApis");
15
18
  const upload_1 = require("./upload");
16
19
  const SendList_1 = require("./SendList");
20
+ const events_1 = __importDefault(require("./events"));
17
21
  function paramsToFilter({ search, wild, searchComment, fileMask }) {
18
22
  search = String(search || '').toLocaleLowerCase();
19
23
  searchComment = String(searchComment || '').toLocaleLowerCase();
@@ -25,6 +29,7 @@ function paramsToFilter({ search, wild, searchComment, fileMask }) {
25
29
  };
26
30
  }
27
31
  const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFiles, admin, ...rest }, ctx) => {
32
+ (0, misc_1.apiAssertTypes)({ string: { uri } });
28
33
  const node = await (0, vfs_1.urlToNode)(uri, ctx);
29
34
  const list = ctx.get('accept') === 'text/event-stream' ? new SendList_1.SendListReadable() : undefined;
30
35
  if (!node)
@@ -81,6 +86,8 @@ const get_file_list = async ({ uri = '/', offset, limit, c, onlyFolders, onlyFil
81
86
  const res = await Promise.all(onDirEntryHandlers.map(cb => cb(cbParams)));
82
87
  if (res.some(x => x === false))
83
88
  continue;
89
+ if ((await events_1.default.emitAsync('dirEntry', cbParams))?.isDefaultPrevented())
90
+ continue;
84
91
  }
85
92
  catch (e) {
86
93
  console.log("a plugin with onDirEntry is causing problems:", e);
package/src/api.lang.js CHANGED
@@ -43,6 +43,7 @@ const apis = {
43
43
  }
44
44
  },
45
45
  async add_langs({ langs }) {
46
+ (0, misc_1.apiAssertTypes)({ object: { langs } });
46
47
  for (let [code, content] of Object.entries(langs)) {
47
48
  code = (0, lang_1.file2code)(code);
48
49
  validateCode(code);
package/src/api.log.js CHANGED
@@ -19,6 +19,7 @@ exports.default = {
19
19
  return { current, rotated: await (0, log_1.getRotatedFiles)() };
20
20
  },
21
21
  async get_log_file({ file = 'log', range = '' }, ctx) {
22
+ (0, misc_1.apiAssertTypes)({ string: { file, range } });
22
23
  const log = lodash_1.default.find(log_1.loggers, { name: file });
23
24
  if (!log)
24
25
  throw cross_1.HTTP_NOT_FOUND;
@@ -33,7 +34,8 @@ exports.default = {
33
34
  return null;
34
35
  },
35
36
  get_log({ file = 'log' }, ctx) {
36
- const files = file.split('|'); // potentially more then one
37
+ (0, misc_1.apiAssertTypes)({ string: { file } });
38
+ const files = file.split('|'); // potentially more than one
37
39
  return new SendList_1.SendListReadable({
38
40
  bufferTime: 10,
39
41
  async doAtStart(list) {
@@ -58,10 +60,10 @@ exports.default = {
58
60
  return list.ready();
59
61
  }
60
62
  // for other logs we only provide updates. Use get_log_file to download past content
61
- if (lodash_1.default.some(files, x => !lodash_1.default.find(log_1.loggers, { name: x })))
63
+ if (lodash_1.default.some(files, x => !lodash_1.default.some(log_1.loggers, { name: x })))
62
64
  return list.error(cross_1.HTTP_NOT_FOUND, true);
63
65
  list.ready();
64
- // unsubscribe when connection is interrupted
66
+ // unsubscribe when the connection is interrupted
65
67
  ctx.res.once('close', events_1.default.on(files, x => list.add(Object.assign(lodash_1.default.pick(x.ctx, ['ip', 'method', 'status']), x, { ctx: undefined }))));
66
68
  }
67
69
  });
@@ -13,6 +13,11 @@ const SendList_1 = require("./SendList");
13
13
  const persistence_1 = require("./persistence");
14
14
  exports.default = {
15
15
  async disconnect({ ip, port, allButLocalhost }) {
16
+ (0, misc_1.apiAssertTypes)({
17
+ string_undefined: { ip },
18
+ number_undefined: { port },
19
+ boolean_undefined: { allButLocalhost },
20
+ });
16
21
  const match = allButLocalhost ? ((x) => !(0, misc_1.isLocalHost)(x.ip))
17
22
  : lodash_1.default.matches({ ip, port });
18
23
  const found = (0, connections_1.getConnections)().filter(c => match(getConnAddress(c)));
package/src/api.net.js CHANGED
@@ -44,6 +44,7 @@ exports.default = {
44
44
  return {};
45
45
  },
46
46
  async map_port({ external, internal }) {
47
+ (0, misc_1.apiAssertTypes)({ number_undefined: { external, internal } });
47
48
  const { upnp, externalPort, internalPort } = await (0, nat_1.getNatInfo)();
48
49
  if (!upnp)
49
50
  return new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE, "upnp failed");
@@ -64,6 +65,7 @@ exports.default = {
64
65
  return {};
65
66
  },
66
67
  async self_check({ url }) {
68
+ (0, misc_1.apiAssertTypes)({ string_undefined: { url } });
67
69
  if (url)
68
70
  return await (0, selfCheck_1.selfCheck)(url)
69
71
  || new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
@@ -79,6 +81,9 @@ exports.default = {
79
81
  return results.length ? results : new apiMiddleware_1.ApiError(const_1.HTTP_SERVICE_UNAVAILABLE);
80
82
  },
81
83
  async make_cert({ domain, email, altNames }) {
84
+ (0, misc_1.apiAssertTypes)({ string: { domain }, string_undefined: { email }, array_undefined: { altNames } });
85
+ if (altNames?.some((name) => typeof name !== 'string'))
86
+ return new apiMiddleware_1.ApiError(const_1.HTTP_BAD_REQUEST, 'bad altNames');
82
87
  await (0, acme_1.makeCert)(domain, email, altNames).catch(e => {
83
88
  throw new apiMiddleware_1.ApiError(const_1.HTTP_SERVER_ERROR, e.message || String(e));
84
89
  });