test_share.js 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. /*
  2. ArozOS Office Suite - share link unit tests (common/share.js)
  3. Run with: node test_share.js (exits 1 on failure)
  4. parse() is the security boundary of ?request=: whatever it returns is
  5. fetched, so most of these pin what it refuses.
  6. */
  7. global.OfficeContainer = require("./container.js");
  8. var S = require("./share.js");
  9. var failures = 0, passes = 0;
  10. function eq(name, got, want) {
  11. if (got === want) { passes++; return; }
  12. failures++;
  13. console.log("FAIL " + name + "\n got: " + JSON.stringify(got) + "\n want: " + JSON.stringify(want));
  14. }
  15. function throws(name, fn) {
  16. try { fn(); } catch (e) { passes++; return; }
  17. failures++;
  18. console.log("FAIL " + name + ": expected an error");
  19. }
  20. var ID = "f7453c19-66c8-4e84-8288-76b84ec0da9f";
  21. var PREVIEW = "http://localhost:8080/share/preview/" + ID + "/";
  22. /* ---- parse: accepted link shapes ---- */
  23. [
  24. ["share page, trailing slash", "http://localhost:8080/share/" + ID + "/", PREVIEW],
  25. ["share page, no slash", "http://localhost:8080/share/" + ID, PREVIEW],
  26. ["preview link", PREVIEW, PREVIEW],
  27. ["download link", "http://localhost:8080/share/download/" + ID + "/HelloWorld.doca", PREVIEW],
  28. ["legacy ?id=", "http://localhost:8080/share?id=" + ID, PREVIEW],
  29. ["surrounding space", " http://localhost:8080/share/" + ID + "/ ", PREVIEW],
  30. ["query and hash ignored", "http://localhost:8080/share/" + ID + "/?x=1#top", PREVIEW],
  31. ["https + reverse-proxy prefix", "https://example.com/aroz/share/" + ID + "/",
  32. "https://example.com/aroz/share/preview/" + ID + "/"],
  33. ["prefix folder called share", "https://example.com/share/share/" + ID + "/",
  34. "https://example.com/share/share/preview/" + ID + "/"]
  35. ].forEach(function (c) {
  36. var info;
  37. try { info = S.parse(c[1]); } catch (e) { eq("parse " + c[0], "threw: " + e.message, c[2]); return; }
  38. eq("parse " + c[0], info.previewUrl, c[2]);
  39. });
  40. eq("download link name hint",
  41. S.parse("http://localhost:8080/share/download/" + ID + "/My%20Doc.doca").nameHint, "My Doc.doca");
  42. eq("share page has no name hint", S.parse("http://localhost:8080/share/" + ID + "/").nameHint, "");
  43. /* ---- parse: refused ---- */
  44. [
  45. ["empty", ""],
  46. ["not a url", "share/" + ID],
  47. ["relative", "/share/" + ID + "/"],
  48. ["javascript scheme", "javascript:alert(1)//share/" + ID],
  49. ["file scheme", "file:///share/" + ID],
  50. ["data scheme", "data:text/plain,share/" + ID],
  51. ["credentials", "http://user:pw@localhost:8080/share/" + ID + "/"],
  52. ["not a share path", "http://localhost:8080/files/" + ID],
  53. ["bad id chars", "http://localhost:8080/share/..%2F..%2Fsystem/"],
  54. ["id too short", "http://localhost:8080/share/abc/"],
  55. ["folder listing", "http://localhost:8080/share/"],
  56. ["other share op", "http://localhost:8080/share/opg/123/" + ID]
  57. ].forEach(function (c) { throws("parse refuses " + c[0], function () { S.parse(c[1]); }); });
  58. /* ---- file names ---- */
  59. eq("name kept", S.fileName("document", ["Report.doca"]), "Report.doca");
  60. eq("first usable wins", S.fileName("document", [null, "", "B.doca"]), "B.doca");
  61. eq("wrong ext replaced", S.fileName("spreadsheet", ["Budget.doca"]), "Budget.xlsa");
  62. eq("no ext added", S.fileName("presentation", ["Pitch"]), "Pitch.ppta");
  63. eq("ext case-insensitive", S.fileName("document", ["A.DOCA"]), "A.DOCA");
  64. eq("default name", S.fileName("spreadsheet", []), "Shared spreadsheet.xlsa");
  65. eq("path stripped", S.fileName("document", ["../../etc/x.doca"]), "x.doca");
  66. eq("backslash path stripped", S.fileName("document", ["C:\\a\\y.doca"]), "y.doca");
  67. eq("control chars stripped", S.fileName("document", ["a\u0000b\n.doca"]), "ab.doca");
  68. eq("bare extension gets default", S.fileName("document", [".doca"]), "Shared document.doca");
  69. /* ---- Content-Disposition ---- */
  70. eq("disposition quoted", S.dispositionName('inline; filename="HelloWorld.doca"'), "HelloWorld.doca");
  71. eq("disposition bare", S.dispositionName("inline; filename=Plain.xlsa"), "Plain.xlsa");
  72. eq("disposition escaped quote", S.dispositionName('inline; filename="say \\"hi\\".ppta"'), 'say "hi".ppta');
  73. eq("disposition rfc2231 wins", S.dispositionName(
  74. "inline; filename*=utf-8''%E5%A0%B1%E5%91%8A.doca; filename=\"fallback.doca\""), "\u5831\u544a.doca");
  75. eq("disposition absent", S.dispositionName(null), "");
  76. eq("disposition without name", S.dispositionName("inline"), "");
  77. /* ---- appOf ---- */
  78. function containerFor(app) {
  79. return OfficeContainer.pack(JSON.stringify({
  80. type: "arozos-office", app: app, version: 1, body: {}
  81. }));
  82. }
  83. eq("appOf document", S.appOf(containerFor("document")), "document");
  84. eq("appOf spreadsheet", S.appOf(containerFor("spreadsheet")), "spreadsheet");
  85. eq("appOf presentation", S.appOf(containerFor("presentation")), "presentation");
  86. eq("appOf unknown app", S.appOf(containerFor("paint")), null);
  87. eq("appOf plain JSON document",
  88. S.appOf(OfficeContainer.utf8Encode('{"app":"spreadsheet","body":{}}')), "spreadsheet");
  89. eq("appOf html page", S.appOf(OfficeContainer.utf8Encode("<!DOCTYPE html><html>")), null);
  90. eq("appOf garbage zip", S.appOf(new Uint8Array([0x50, 0x4B, 1, 2, 3])), null);
  91. eq("appOf empty", S.appOf(new Uint8Array(0)), null);
  92. console.log(passes + " passed, " + failures + " failed");
  93. process.exit(failures ? 1 : 0);