Compare commits

..

286 Commits

Author SHA1 Message Date
dependabot[bot] 0b03952404 build(deps): bump github/codeql-action from 3.29.7 to 4.31.2
Bumps [github/codeql-action](https://github.com/github/codeql-action) from 3.29.7 to 4.31.2.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/51f77329afa6477de8c49fc9c7046c15b9a4e79d...0499de31b99561a6d14a36a5f662c2a54f91beee)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.31.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-11-01 06:16:37 +00:00
dependabot[bot] 364e273836 build(deps): bump github/codeql-action from 3.28.19 to 3.29.5 (#133)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 11:22:39 +02:00
dependabot[bot] c4cde95e31 build(deps): bump step-security/harden-runner from 2.12.0 to 2.13.0 (#132)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-08-01 11:21:02 +02:00
Sebastian Beltran b3cf4a76ce chore: add funding to package.json (#131) 2025-07-23 14:46:10 +02:00
dependabot[bot] 988bc1109c build(deps): bump ossf/scorecard-action from 2.4.0 to 2.4.2 (#123)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:13:38 +02:00
dependabot[bot] 411920523e build(deps): bump actions/upload-artifact from 4.5.0 to 4.6.2 (#122)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:13:06 +02:00
dependabot[bot] 068b999804 build(deps): bump coverallsapp/github-action from 1.2.5 to 2.3.6 (#121)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:12:25 +02:00
dependabot[bot] af5eb8e7c3 build(deps): bump github/codeql-action from 3.27.9 to 3.28.19 (#124)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-06-07 12:11:38 +02:00
Ulises Gascón 1696289791 ci: upgrade Node versions (#128) 2025-06-07 12:06:49 +02:00
Ulises Gascón d8efc0e652 ci: apply OSSF Scorecard security best practices (#119)
Co-authored-by: Ulises Gascón <ulisesgascongonzalez@gmail.com>
Co-authored-by: StepSecurity Bot <bot@stepsecurity.io>
2025-06-05 14:40:58 +02:00
Carlos Serrano 6729afa6d2 chore: upgrade scorecard workflow pinned action versions (#112) 2025-04-17 16:17:01 +02:00
Íñigo Marquínez Prado 544279a064 ci: migrate to GitHub actions and add missing Node.js versions (#109)
- PR-URL: https://github.com/expressjs/serve-index/pull/109
- This commit drops support for Travis
2024-05-14 18:28:45 +02:00
Íñigo Marquínez Prado 7b27f08fac chore: add support for OSSF scorecard reporting (#110)
PR-URL: https://github.com/expressjs/serve-index/pull/110
2024-05-14 18:18:01 +02:00
Douglas Christopher Wilson d2922cd872 deps: mime-types@~2.1.30 2021-04-02 22:20:37 -04:00
Douglas Christopher Wilson 51b376a02d build: use nyc for test coverage 2021-04-01 21:41:49 -04:00
Douglas Christopher Wilson 4e7785c620 Fix icons for MIME types with "+"
fixes #99
2021-04-01 16:40:42 -04:00
Douglas Christopher Wilson 7bc2da919b build: eslint@7.23.0 2021-04-01 16:35:32 -04:00
Douglas Christopher Wilson 1e07314bb3 build: Node.js@15.13 2021-03-31 20:54:39 -04:00
Douglas Christopher Wilson b14fff17ff build: Node.js@12.22 2021-03-31 20:48:19 -04:00
Douglas Christopher Wilson b74579da7b build: eslint-plugin-markdown@2.0.0 2021-03-17 19:57:35 -04:00
Douglas Christopher Wilson b827e29cc0 build: mocha@8.3.2 2021-03-17 19:44:33 -04:00
Douglas Christopher Wilson 783928d36d build: Node.js@15.11 2021-03-15 16:33:41 -04:00
Douglas Christopher Wilson c26a84ccdb build: eslint@7.21.0 2021-02-28 00:06:53 -05:00
Douglas Christopher Wilson 6410f231d2 build: mocha@8.3.0 2021-02-27 23:48:31 -05:00
Douglas Christopher Wilson 1230f3a2a4 build: Node.js@15.10 2021-02-27 23:38:30 -05:00
Douglas Christopher Wilson 5a6402bc62 deps: mime-types@~2.1.29 2021-02-27 23:31:53 -05:00
Douglas Christopher Wilson 55c94d53d7 build: eslint@7.20.0 2021-02-25 01:24:29 -05:00
Douglas Christopher Wilson 85a41c6169 build: Node.js@10.24 2021-02-25 01:22:03 -05:00
Douglas Christopher Wilson c22ac7619b build: Node.js@12.21 2021-02-24 20:10:12 -05:00
Douglas Christopher Wilson 410d2e0336 build: Node.js@14.16 2021-02-24 20:08:10 -05:00
Douglas Christopher Wilson 395c961e7d build: Node.js@15.8 2021-02-03 00:11:40 -05:00
Douglas Christopher Wilson 91a3cd9ac1 build: eslint@7.19.0 2021-02-02 23:51:13 -05:00
Douglas Christopher Wilson 0812a30474 build: Node.js@15.7 2021-01-27 19:36:40 -05:00
Douglas Christopher Wilson 6a3fdb0dae build: supertest@6.1.3 2021-01-27 19:33:08 -05:00
Douglas Christopher Wilson 8af3044675 build: eslint@7.17.0 2021-01-04 00:12:51 -05:00
Douglas Christopher Wilson be96563b6b build: Node.js@15.5 2021-01-04 00:09:54 -05:00
Douglas Christopher Wilson 22828c2005 deps: mime-types@~2.1.28 2021-01-03 23:55:38 -05:00
Douglas Christopher Wilson 9c8d7e70c3 build: eslint@7.15.0 2020-12-13 23:51:58 -05:00
Douglas Christopher Wilson ac3c870ad2 build: Node.js@15.3 2020-11-27 19:09:34 -05:00
Douglas Christopher Wilson 546b996dad build: supertest@6.0.1 2020-11-18 00:48:39 -05:00
Douglas Christopher Wilson de3bcd39c9 build: mocha@8.2.1 2020-11-18 00:42:18 -05:00
Douglas Christopher Wilson 4bd3a75480 build: Node.js@12.20 2020-11-17 20:04:08 -05:00
Douglas Christopher Wilson 0f4a3bd370 build: eslint@7.13.0 2020-11-16 17:59:52 -05:00
Douglas Christopher Wilson 438feabf50 build: support Node.js 15.x 2020-11-07 18:44:40 -05:00
Douglas Christopher Wilson 07e47df692 build: supertest@6.0.0 2020-11-01 21:22:22 -05:00
Douglas Christopher Wilson 81afa23b29 build: Node.js@14.15 2020-10-30 22:07:21 -04:00
Douglas Christopher Wilson cad6e104ef build: eslint@7.12.1 2020-10-29 22:09:34 -04:00
Douglas Christopher Wilson 947fc1bbc8 build: mocha@8.2.0 2020-10-27 20:41:15 -04:00
Douglas Christopher Wilson 30ae682927 build: Node.js@14.14 2020-10-24 00:32:57 -04:00
Douglas Christopher Wilson b9469ab500 build: Node.js@10.23 2020-10-24 00:29:06 -04:00
Douglas Christopher Wilson 2fb9ab3260 build: Node.js@12.19 2020-10-23 20:21:44 -04:00
Douglas Christopher Wilson 9f931d53f9 build: eslint@7.10.0 2020-10-05 00:51:51 -04:00
Douglas Christopher Wilson 3884c54c00 build: Node.js@14.13 2020-10-04 20:01:27 -04:00
Douglas Christopher Wilson e161893d4a build: eslint@7.8.1 2020-09-10 00:13:18 -04:00
Douglas Christopher Wilson 006327c26a build: mocha@8.1.3 2020-09-10 00:10:02 -04:00
Douglas Christopher Wilson ed99750527 build: Node.js@14.10 2020-09-09 23:08:59 -04:00
Douglas Christopher Wilson 5f5124a79a build: eslint@7.6.0 2020-08-05 22:40:12 -04:00
Douglas Christopher Wilson b7186ae2db build: Node.js@14.7 2020-08-03 20:16:43 -04:00
Douglas Christopher Wilson 6ed2cab63e build: mocha@8.1.0 2020-08-03 20:11:18 -04:00
Douglas Christopher Wilson 7b37172d09 build: Node.js@10.22 2020-07-18 19:44:10 -04:00
Douglas Christopher Wilson bfef3ecf2a deps: http-errors@~1.8.0 2020-07-17 00:07:45 -04:00
Douglas Christopher Wilson 4fbef9d380 build: eslint@7.4.0 2020-07-16 20:29:49 -04:00
Douglas Christopher Wilson c32caf8a37 build: Node.js@14.5 2020-07-16 20:25:24 -04:00
Douglas Christopher Wilson 8e68e57125 build: mocha@8.0.1 2020-06-13 22:01:34 -04:00
Douglas Christopher Wilson f3d18ab5ce build: Node.js@12.18 2020-06-11 23:07:26 -04:00
Douglas Christopher Wilson b7b10be97d build: Node.js@10.21 2020-06-10 19:08:41 -04:00
Douglas Christopher Wilson d416ee987e build: eslint@7.2.0 2020-06-06 17:48:33 -04:00
Douglas Christopher Wilson f15cbc8386 build: Node.js@14.4 2020-06-06 17:44:03 -04:00
Douglas Christopher Wilson 5c959be72f build: Node.js@12.17 2020-05-30 21:34:02 -04:00
Douglas Christopher Wilson a92a2a10d7 build: eslint@7.1.0 2020-05-25 22:19:22 -04:00
Douglas Christopher Wilson a4fe4bcf96 build: mocha@7.2.0 2020-05-23 19:09:35 -04:00
Douglas Christopher Wilson fe7aa00af8 build: Node.js@14.3 2020-05-23 19:04:19 -04:00
Douglas Christopher Wilson 7e737fb9f4 build: eslint@7.0.0 2020-05-17 23:08:37 -04:00
Douglas Christopher Wilson ba0c70c6b5 build: Node.js@14.1 2020-05-01 21:52:54 -04:00
Douglas Christopher Wilson ee51d44121 build: mocha@7.1.2 2020-04-30 20:28:53 -04:00
Douglas Christopher Wilson 313678fdc4 build: Node.js@13.14 2020-04-29 00:08:09 -04:00
Douglas Christopher Wilson 4921806568 build: support Node.js 14.x 2020-04-28 23:11:13 -04:00
Douglas Christopher Wilson bc57152e09 deps: mime-types@~2.1.27 2020-04-28 23:08:00 -04:00
Douglas Christopher Wilson 27facca566 build: Node.js@10.20 2020-04-20 21:58:49 -04:00
Douglas Christopher Wilson 13f7bc57a2 build: Node.js@13.13 2020-04-15 16:09:24 -04:00
Douglas Christopher Wilson c1db68ba39 build: mocha@7.1.1 2020-04-06 17:50:45 -04:00
Douglas Christopher Wilson cb8b5c60f4 build: eslint-plugin-markdown@1.0.2 2020-03-14 17:44:20 -04:00
Douglas Christopher Wilson 51372447e0 build: Node.js@13.10 2020-03-11 20:41:28 -04:00
Douglas Christopher Wilson 39e8b5782b build: mocha@7.1.0 2020-03-09 23:04:24 -04:00
Douglas Christopher Wilson fc7be1bfb5 build: Node.js@12.16 2020-02-14 22:59:12 -05:00
Douglas Christopher Wilson 38768868ea build: remove deprecated Travis CI directive 2020-02-14 22:58:50 -05:00
Douglas Christopher Wilson cd937079b3 build: Node.js@13.8 2020-02-08 20:06:33 -05:00
Douglas Christopher Wilson 4f0326af87 build: Node.js@12.15 2020-02-07 22:42:08 -05:00
Douglas Christopher Wilson d0e883a3df build: Node.js@10.19 2020-02-07 22:41:01 -05:00
Douglas Christopher Wilson 105ca237f6 build: Node.js@13.7 2020-02-05 19:52:35 -05:00
Douglas Christopher Wilson 4d34c9b736 build: mocha@7.0.1 2020-02-05 19:51:54 -05:00
Douglas Christopher Wilson 34fffcb92c build: Node.js@13.6 2020-01-12 01:28:42 -05:00
Douglas Christopher Wilson 86e87f8c5e deps: mime-types@~2.1.26 2020-01-12 01:21:44 -05:00
Douglas Christopher Wilson b08cbbabd4 build: mocha@7.0.0 2020-01-11 21:42:39 -05:00
Douglas Christopher Wilson 34aa14a456 build: Node.js@13.5 2019-12-24 15:27:14 -05:00
Douglas Christopher Wilson 9b69727ad2 build: eslint@6.8.0 2019-12-24 15:20:44 -05:00
Douglas Christopher Wilson 82b74e75bf build: Node.js@12.14 2019-12-19 00:28:26 -05:00
Douglas Christopher Wilson 544b6e49a2 build: Node.js@10.18 2019-12-19 00:21:13 -05:00
Douglas Christopher Wilson 84660635a2 build: Node.js@8.17 2019-12-18 21:54:37 -05:00
Douglas Christopher Wilson 8336655fcb build: Node.js@13.3 2019-12-05 18:44:26 -05:00
Douglas Christopher Wilson 6044ccbd7d build: eslint@6.7.2 2019-12-03 19:38:41 -05:00
Douglas Christopher Wilson 2f549f0bab build: Node.js@13.2 2019-11-22 00:10:20 -05:00
Douglas Christopher Wilson 87aa9ef47c deps: mime-types@~2.1.25 2019-11-21 23:39:17 -05:00
Douglas Christopher Wilson 327056b44b build: eslint-plugin-markdown@1.0.1 2019-11-19 22:54:51 -05:00
Douglas Christopher Wilson 6e1bcada54 build: Node.js@13.1 2019-11-09 20:44:00 -05:00
Douglas Christopher Wilson 4c64e7b791 build: mocha@6.2.2 2019-11-06 17:05:11 -05:00
Douglas Christopher Wilson 823d804e58 build: support Node.js 13.x 2019-11-04 19:52:20 -05:00
Douglas Christopher Wilson 8e1d258ad1 build: Node.js@12.13 2019-11-02 00:50:02 -04:00
Douglas Christopher Wilson c24512e404 build: Node.js@10.17 2019-11-02 00:43:03 -04:00
Douglas Christopher Wilson a15e3fbb0c build: eslint@6.6.0 2019-11-01 23:51:01 -04:00
Douglas Christopher Wilson 58bfa078c7 build: fix eslint uninstall failures 2019-09-23 00:41:20 -04:00
Douglas Christopher Wilson 9f0a8519f5 build: supertest@4.0.2 2019-09-22 23:07:53 -04:00
Douglas Christopher Wilson 214eea3175 build: Node.js@12.10 2019-09-12 21:50:06 -04:00
Douglas Christopher Wilson 018fa454e0 build: eslint@6.3.0 2019-09-10 19:45:54 -04:00
Douglas Christopher Wilson 4d8b4917e7 build: mocha@6.2.0 2019-09-10 19:40:18 -04:00
Douglas Christopher Wilson 2b82ea68fc build: Node.js@12.8 2019-08-10 16:50:24 -04:00
Douglas Christopher Wilson 3ab32ac667 build: eslint@6.1.0 2019-08-10 16:47:59 -04:00
Douglas Christopher Wilson 71b655e777 build: mocha@5.2.0 2019-08-08 23:19:32 -04:00
Douglas Christopher Wilson 2802ec6f2f build: supertest@3.4.2 2019-08-06 01:22:44 -04:00
Douglas Christopher Wilson 54e0eb7115 build: Node.js@12.7 2019-08-05 19:38:35 -04:00
Douglas Christopher Wilson fc6d3eadac build: mocha@4.1.0 2019-07-17 19:44:53 -04:00
Douglas Christopher Wilson e3ff991be9 build: supertest@2.0.0 2019-07-15 22:09:35 -04:00
Douglas Christopher Wilson 8fbb732bde build: mocha@3.5.3 2019-07-14 23:07:26 -04:00
Douglas Christopher Wilson 00f3ef67a6 build: restructure CI build steps 2019-07-13 21:55:53 -04:00
Douglas Christopher Wilson 5271288d1a build: eslint@5.16.0 2019-07-10 17:49:45 -04:00
Douglas Christopher Wilson bda73e36c2 build: eslint-plugin-markdown@1.0.0 2019-07-06 18:52:56 -04:00
Douglas Christopher Wilson 9c0287c406 deps: http-errors@~1.7.3 2019-07-05 00:44:00 -04:00
Douglas Christopher Wilson 136fe69c11 build: Node.js@10.16 2019-07-05 00:27:24 -04:00
Douglas Christopher Wilson 0dde93dc69 build: Node.js@12.6 2019-07-04 23:51:06 -04:00
Douglas Christopher Wilson 12a56a6f2e build: eslint@4.19.1 2019-07-04 19:43:32 -04:00
Douglas Christopher Wilson 9569cb6852 tests: move asserts so supertest can catch 2019-06-17 20:43:06 -04:00
Douglas Christopher Wilson 20e83c893b build: support Node.js 12.x 2019-05-02 23:37:45 -04:00
Douglas Christopher Wilson 4c8c716ded build: Node.js@11.15 2019-05-02 23:37:20 -04:00
Douglas Christopher Wilson 002a251122 Fix text and json responses to match html sorting 2019-05-02 23:35:13 -04:00
Douglas Christopher Wilson 5583f9ec06 build: Node.js@11.14 2019-04-29 23:55:03 -04:00
Douglas Christopher Wilson 4482ec9639 build: Node.js@8.16 2019-04-29 23:54:49 -04:00
Douglas Christopher Wilson e7ae813459 build: eslint-plugin-markdown@1.0.0-beta.8 2019-04-29 23:54:04 -04:00
Douglas Christopher Wilson fa3fe02a2f deps: accepts@~1.3.7 2019-04-29 23:52:19 -04:00
Douglas Christopher Wilson a30f269f99 deps: parseurl@~1.3.3 2019-04-28 00:56:30 -04:00
Douglas Christopher Wilson d8ff9b9e41 deps: mime-types@~2.1.24 2019-04-28 00:55:52 -04:00
Joonas Rouhiainen d3ee7d8f55 Use 400 error on URI decode failure instead of 500
closes #85
closes #88
2019-04-27 23:18:36 -04:00
Douglas Christopher Wilson 32b7a1adec deps: http-errors@~1.7.2 2019-04-01 00:45:20 -04:00
Douglas Christopher Wilson 173f71a6a1 deps: mime-types@~2.1.22 2019-04-01 00:38:48 -04:00
Douglas Christopher Wilson 4382b8f625 build: Node.js@11.13 2019-03-31 23:02:00 -04:00
Douglas Christopher Wilson 6636afe7fd build: Node.js@6.17 2019-03-31 22:54:48 -04:00
Douglas Christopher Wilson 156303efe0 deps: http-errors@~1.7.1 2019-01-04 21:40:16 -05:00
Douglas Christopher Wilson 9c56901718 build: support Node.js 11.x 2019-01-03 17:02:48 -05:00
Douglas Christopher Wilson 935ee09c82 build: Node.js@10.15 2018-12-30 00:14:12 -05:00
Douglas Christopher Wilson 05b0e21848 build: Node.js@8.15 2018-12-30 00:09:51 -05:00
Douglas Christopher Wilson fadb156765 build: Node.js@6.16 2018-12-30 00:02:39 -05:00
Douglas Christopher Wilson abedc7fcfe deps: mime-types@~2.1.21 2018-12-25 23:50:40 -05:00
Douglas Christopher Wilson 92d67676ff build: support Node.js 10.x 2018-06-09 19:26:38 -04:00
Douglas Christopher Wilson fcad6767c0 build: Node.js@9.11 2018-06-04 00:34:22 -04:00
Douglas Christopher Wilson 9bf00b76ad deps: http-errors@~1.6.3 2018-06-04 00:21:18 -04:00
Douglas Christopher Wilson 98107736e0 deps: accepts@~1.3.5 2018-06-03 23:39:21 -04:00
Douglas Christopher Wilson 77919cf57b build: Node.js@8.11 2018-05-29 12:07:12 -04:00
Douglas Christopher Wilson 010b8c9124 build: Node.js@6.14 2018-05-26 00:08:11 -04:00
Douglas Christopher Wilson b215084582 build: Node.js@4.9 2018-05-26 00:03:32 -04:00
Douglas Christopher Wilson 451dc90796 build: fix Node.js 0.8 appveyor build 2018-05-25 23:49:02 -04:00
Douglas Christopher Wilson 9d29665ce3 Show font icon for more font types 2018-02-21 23:24:56 -05:00
Douglas Christopher Wilson f31db046f6 deps: mime-types@~2.1.18 2018-02-21 23:01:11 -05:00
Douglas Christopher Wilson c67b828cbc lint: remove usages of "==" 2017-11-23 00:08:40 -05:00
Douglas Christopher Wilson 7fc9fe1f7b build: support Node.js 9.x 2017-11-23 00:02:38 -05:00
Douglas Christopher Wilson cec35eed49 build: Node.js@8.9 2017-11-22 23:59:29 -05:00
Douglas Christopher Wilson 561f159d00 build: Node.js@6.12 2017-11-22 23:58:48 -05:00
Douglas Christopher Wilson d66e8036f7 lint: add editorconfig and eslint to enforce 2017-11-22 23:54:40 -05:00
Douglas Christopher Wilson b0f8f33d4a docs: remove gratipay badge 2017-11-22 23:30:04 -05:00
Douglas Christopher Wilson a399faa180 1.9.1 2017-09-29 00:23:59 -04:00
Douglas Christopher Wilson fc6953383f deps: parseurl@~1.3.2 2017-09-29 00:22:44 -04:00
Douglas Christopher Wilson 45dbe4f219 docs: add express.static to the express example
closes #64
2017-09-29 00:20:54 -04:00
Daniel Tschinder 76e3c3af41 deps: debug@2.6.9
closes #65
2017-09-28 23:27:29 -04:00
Douglas Christopher Wilson da37631e2c build: Node.js@8.3 2017-09-02 20:01:11 -04:00
Douglas Christopher Wilson 3e09fb26f9 build: Node.js@6.11 2017-09-02 19:58:07 -04:00
Douglas Christopher Wilson 0894f8c9ff deps: mime-types@~2.1.17 2017-09-02 19:54:30 -04:00
Douglas Christopher Wilson a4f1ef3d5e deps: http-errors@~1.6.2 2017-09-02 19:48:04 -04:00
Douglas Christopher Wilson ccbeaebe69 deps: accepts@~1.3.4 2017-09-02 19:45:33 -04:00
Douglas Christopher Wilson bc077cbe29 1.9.0 2017-05-25 15:45:44 -04:00
Douglas Christopher Wilson 9ecc9a68f9 Set X-Content-Type-Options: nosniff header 2017-05-25 01:11:36 -04:00
Douglas Christopher Wilson 2b7755a28b Refactor responding to common function 2017-05-25 01:07:35 -04:00
Douglas Christopher Wilson 506626c751 lint: remove two unused variable declarations 2017-05-25 00:51:11 -04:00
Douglas Christopher Wilson 2349675b98 build: Node.js@7.10 2017-05-25 00:45:05 -04:00
Douglas Christopher Wilson c5a317fb45 deps: batch@0.6.1 2017-05-25 00:44:21 -04:00
Douglas Christopher Wilson 9284264604 deps: debug@2.6.8
closes #63
2017-05-25 00:42:30 -04:00
Douglas Christopher Wilson c2585e2c16 build: Node.js@7.9 2017-04-28 22:09:36 -04:00
Douglas Christopher Wilson e7d4062773 tests: remove unused file 2017-04-25 23:17:40 -04:00
Douglas Christopher Wilson 3e32e4ac3d build: Node.js@7.8 2017-04-25 23:13:11 -04:00
Lucian Buzzo 69137b760f deps: debug@2.6.4
closes #62
2017-04-25 23:11:41 -04:00
Douglas Christopher Wilson a8205c834a build: Node.js@7.7 2017-03-25 22:48:16 -04:00
Douglas Christopher Wilson d09bb5e774 deps: batch@0.6.0 2017-03-25 22:45:23 -04:00
Douglas Christopher Wilson 6024e991ca deps: debug@2.6.3 2017-03-25 22:35:02 -04:00
Douglas Christopher Wilson f7e0bb551d deps: mime-types@~2.1.15 2017-03-25 22:32:39 -04:00
Douglas Christopher Wilson 7215f76cca build: Node.js@7.6 2017-03-08 00:17:05 -05:00
Douglas Christopher Wilson c6b63e4666 build: Node.js@6.10 2017-03-08 00:15:37 -05:00
Douglas Christopher Wilson ec31019c9e build: Node.js@4.8 2017-03-08 00:09:12 -05:00
Douglas Christopher Wilson efe0c6fa7d deps: http-errors@~1.6.1 2017-03-08 00:04:36 -05:00
Douglas Christopher Wilson d2bebdbd87 deps: mime-types@~2.1.14 2017-03-02 22:33:47 -05:00
Douglas Christopher Wilson 3048085dc4 deps: debug@2.6.1 2017-03-02 22:31:26 -05:00
Douglas Christopher Wilson 1c8c8b9a59 build: Node.js@7.4 2017-02-08 00:37:09 -05:00
Douglas Christopher Wilson 2bbaaba5cc build: Node.js@4.7 2017-02-08 00:35:08 -05:00
Douglas Christopher Wilson 84285a0826 build: support Node.js 7.x 2016-11-21 20:31:31 -05:00
Douglas Christopher Wilson 11c2b39640 deps: mime-types@~2.1.13 2016-11-21 20:30:36 -05:00
Douglas Christopher Wilson 98ecd80f57 deps: http-errors@~1.5.1 2016-11-21 20:29:37 -05:00
Douglas Christopher Wilson 53a7b06bc6 deps: debug@2.3.3 2016-11-20 22:11:16 -05:00
Douglas Christopher Wilson 4ed20255be build: Node.js@6.9 2016-11-20 22:08:36 -05:00
Douglas Christopher Wilson 8201055cba build: Node.js@4.6 2016-11-20 22:05:47 -05:00
Douglas Christopher Wilson af6bec9cc3 docs: add preamble to install section 2016-09-15 19:16:03 +02:00
Douglas Christopher Wilson 2932faa568 build: istanbul@0.4.5 2016-09-15 19:10:35 +02:00
Douglas Christopher Wilson dc54507b68 build: after@0.8.2 2016-09-15 19:06:24 +02:00
Douglas Christopher Wilson 72b08d6395 build: Node.js@4.5 2016-09-15 19:04:36 +02:00
Douglas Christopher Wilson 1284ad0a9f build: istanbul@0.4.4 2016-08-20 21:48:50 -04:00
Douglas Christopher Wilson a275e385c4 build: Node.js@5.12 2016-08-20 21:43:35 -04:00
Douglas Christopher Wilson 357452911d 1.8.0 2016-06-17 12:13:08 -04:00
Douglas Christopher Wilson fb1c509620 build: support Node.js 6.x 2016-06-16 20:37:26 -04:00
Douglas Christopher Wilson 2e1836e614 build: Node.js@5.11 2016-06-16 20:37:07 -04:00
Douglas Christopher Wilson ed181e36ed deps: http-errors@~1.5.0 2016-06-16 19:46:25 -04:00
Douglas Christopher Wilson f99abfe86a build: mocha@2.5.3 2016-06-16 19:44:37 -04:00
Douglas Christopher Wilson e9ccd54a5d build: istanbul@0.4.3 2016-06-16 19:43:53 -04:00
Douglas Christopher Wilson 972e4d1ced deps: accepts@~1.3.3
fixes #56
2016-06-16 19:42:47 -04:00
Douglas Christopher Wilson 28e28c8352 deps: mime-types@~2.1.11
closes #47
2016-06-16 19:41:07 -04:00
Douglas Christopher Wilson cd7ab38593 build: cache node_modules on CI 2016-04-17 22:19:04 -04:00
Douglas Christopher Wilson a8c6d17b08 build: Node.js@5.10 2016-04-17 22:11:58 -04:00
Douglas Christopher Wilson 6cb3ab01a0 build: Node.js@4.4 2016-04-17 22:09:43 -04:00
Douglas Christopher Wilson 11b27ed4d3 build: mocha@2.4.5 2016-02-13 16:09:46 -05:00
Patrick Glynn 5afc181207 Make inline file search case-insensitive
closes #38
closes #45
2016-01-24 18:51:15 -05:00
Douglas Christopher Wilson 8195aa5d6a deps: accepts@~1.3.1 2016-01-24 18:45:30 -05:00
Douglas Christopher Wilson fc9db6b56a 1.7.3 2016-01-24 18:28:28 -05:00
Douglas Christopher Wilson ac1148a50f deps: mime-types@~2.1.9 2016-01-24 18:18:25 -05:00
Douglas Christopher Wilson edc865cb7d deps: parseurl@~1.3.1 2016-01-24 18:17:51 -05:00
Douglas Christopher Wilson 22e7f31395 build: istanbul@0.4.2 2016-01-24 18:16:50 -05:00
Douglas Christopher Wilson dd48453baa build: support Node.js 5.x 2016-01-03 15:18:22 -05:00
Douglas Christopher Wilson 26cc6d91c0 deps: mime-types@~2.1.8 2016-01-03 15:15:03 -05:00
Douglas Christopher Wilson 735de3e4c0 deps: escape-html@~1.0.3 2016-01-03 15:12:38 -05:00
Douglas Christopher Wilson 3ce952cfd5 deps: accepts@~1.2.13 2016-01-03 15:09:36 -05:00
Douglas Christopher Wilson 7820063014 deps: batch@0.5.3 2015-12-30 16:36:01 -05:00
Douglas Christopher Wilson 5101c6373c build: supertest@1.1.0 2015-12-30 16:28:10 -05:00
Douglas Christopher Wilson b326b6dfa4 build: mocha@2.3.4 2015-12-30 16:25:31 -05:00
Douglas Christopher Wilson 38a1ab69fa build: istanbul@0.4.1 2015-12-30 16:19:41 -05:00
Douglas Christopher Wilson 191f4b10a3 build: support Node.js 4.x 2015-12-30 16:16:37 -05:00
Douglas Christopher Wilson a93f8002a9 build: support io.js 3.x 2015-12-30 16:15:04 -05:00
Douglas Christopher Wilson d3025b9744 build: io.js@2.5 2015-12-30 16:13:59 -05:00
Douglas Christopher Wilson ac6ea7515a build: reduce runtime versions to one per major 2015-12-30 16:12:44 -05:00
Douglas Christopher Wilson b95016be37 build: skip istanbul coverage on Node.js 0.8 2015-12-30 16:09:36 -05:00
Douglas Christopher Wilson f39ad778ed 1.7.2 2015-07-31 00:06:52 -04:00
Douglas Christopher Wilson 73413c4caa deps: mime-types@~2.1.4 2015-07-30 23:42:56 -04:00
Douglas Christopher Wilson 6711bfa431 deps: accepts@~1.2.12 2015-07-30 23:42:19 -04:00
Douglas Christopher Wilson f17363a45d 1.7.1 2015-07-05 23:32:34 -04:00
Douglas Christopher Wilson db2163892e deps: mime-types@~2.1.2 2015-07-05 23:30:07 -04:00
Douglas Christopher Wilson 5c74d1e44a deps: accepts@~1.2.10 2015-07-05 23:29:30 -04:00
Douglas Christopher Wilson bd6f5553a5 build: io.js@2.3 2015-07-05 23:28:22 -04:00
Douglas Christopher Wilson 63b45e6208 1.7.0 2015-06-15 16:50:22 -04:00
Douglas Christopher Wilson d680e6302d Accept function value for template option
closes #20
closes #30
2015-06-14 23:07:52 -04:00
Douglas Christopher Wilson 420d159be7 Stat parent directory when necessary 2015-06-14 22:29:58 -04:00
Douglas Christopher Wilson 6d3160a32c Use Date.prototype.toLocaleDateString to format date 2015-06-14 22:01:22 -04:00
Douglas Christopher Wilson 4056d3375c deps: mime-types@~2.1.1 2015-06-14 21:17:01 -04:00
Douglas Christopher Wilson 7566e487b0 deps: accepts@~1.2.9 2015-06-14 21:14:45 -04:00
Douglas Christopher Wilson 3ae1ea56d3 perf: remove argument reassignment 2015-06-14 21:13:27 -04:00
Douglas Christopher Wilson d60c7a0055 Send non-chunked response for OPTIONS 2015-06-14 21:01:42 -04:00
Douglas Christopher Wilson 6781d9536b perf: enable strict mode 2015-06-14 21:00:45 -04:00
Douglas Christopher Wilson ea919ec568 deps: escape-html@1.0.2 2015-06-14 20:59:38 -04:00
Douglas Christopher Wilson 07611f42ad build: supertest@1.0.1 2015-06-14 20:56:13 -04:00
Douglas Christopher Wilson e4355f0e97 build: mocha@2.2.5 2015-06-14 20:55:24 -04:00
Douglas Christopher Wilson 5275b835a7 build: io.js@2.1 2015-06-14 20:54:28 -04:00
Douglas Christopher Wilson 636a8ae7bc 1.6.4 2015-05-12 21:28:44 -04:00
Douglas Christopher Wilson 320226f35a deps: mime-types@~2.0.11 2015-05-12 21:00:31 -04:00
Douglas Christopher Wilson 2003c3f743 deps: mocha@~2.2.4 2015-05-12 20:59:49 -04:00
Douglas Christopher Wilson e385089155 deps: debug@~2.2.0 2015-05-12 20:59:15 -04:00
Douglas Christopher Wilson 5462db3946 deps: accepts@~1.2.7 2015-05-12 20:58:24 -04:00
Douglas Christopher Wilson cb60444cb1 deps: istanbul@0.3.9 2015-05-12 20:57:39 -04:00
Douglas Christopher Wilson 6691f1b75e build: support io.js 2.x 2015-05-12 14:46:31 -04:00
Douglas Christopher Wilson 5cb8bcc448 build: io.js@1.8 2015-05-12 13:14:15 -04:00
Douglas Christopher Wilson e11f253226 1.6.3 2015-03-14 01:22:30 -04:00
Douglas Christopher Wilson 7381a1d145 Properly escape file names in HTML
fixes #28
2015-03-13 22:34:00 -04:00
Douglas Christopher Wilson 6a4a87edcd build: support io.js 1.x 2015-03-13 22:25:52 -04:00
Douglas Christopher Wilson 99e8ec512a deps: mime-types@~2.0.10 2015-03-13 22:24:20 -04:00
Douglas Christopher Wilson 197d9f6a0f deps: accepts@~1.2.5 2015-03-13 22:23:39 -04:00
Douglas Christopher Wilson 41fe20602a deps: debug@~2.1.3 2015-03-13 21:33:11 -04:00
Douglas Christopher Wilson b5c886f36d deps: mocha@~2.2.1 2015-03-12 19:03:04 -04:00
Douglas Christopher Wilson 6da896f6d8 deps: istanbul@0.3.7 2015-03-11 22:43:09 -04:00
Douglas Christopher Wilson 5ec9698c08 1.6.2 2015-02-16 22:50:32 -05:00
Douglas Christopher Wilson 4da905e4d3 deps: http-errors@~1.3.1 2015-02-16 19:50:16 -05:00
Douglas Christopher Wilson 62ac6f345b deps: mime-types@~2.0.9 2015-02-16 19:49:09 -05:00
Douglas Christopher Wilson 9afe1a7816 deps: accepts@~1.2.4 2015-02-16 19:48:10 -05:00
Douglas Christopher Wilson 0368c20a58 docs: update badges 2015-02-16 19:46:45 -05:00
Douglas Christopher Wilson 66a13d0814 build: support Node.js 0.12 2015-02-16 19:45:12 -05:00
Douglas Christopher Wilson b8d8f4524e build: use Travis CI container infrastructure 2015-02-16 19:44:34 -05:00
Douglas Christopher Wilson 75efc2d68e build: use faster AppVeyor Node.js switching 2015-02-16 19:44:04 -05:00
22 changed files with 1461 additions and 435 deletions
+11
View File
@@ -0,0 +1,11 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
insert_final_newline = true
trim_trailing_whitespace = true
[{*.js,*.json,*.yml}]
indent_size = 2
indent_style = space
+3
View File
@@ -0,0 +1,3 @@
.nyc_output
coverage
node_modules
+13
View File
@@ -0,0 +1,13 @@
root: true
extends:
- plugin:markdown/recommended
plugins:
- markdown
overrides:
- files: '**/*.md'
processor: 'markdown/markdown'
rules:
eol-last: error
eqeqeq: [error, allow-null]
indent: [error, 2, { SwitchCase: 1 }]
no-trailing-spaces: error
+15
View File
@@ -0,0 +1,15 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: monthly
- package-ecosystem: npm
directory: /
schedule:
interval: monthly
open-pull-requests-limit: 10
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-major"]
+241
View File
@@ -0,0 +1,241 @@
name: ci
on:
- pull_request
- push
permissions:
contents: read
jobs:
test:
permissions:
checks: write # for coverallsapp/github-action to create new checks
contents: read # for actions/checkout to fetch code
runs-on: ubuntu-latest
strategy:
matrix:
name:
- Node.js 0.8
- Node.js 0.10
- Node.js 0.12
- io.js 1.x
- io.js 2.x
- io.js 3.x
- Node.js 4.x
- Node.js 5.x
- Node.js 6.x
- Node.js 7.x
- Node.js 8.x
- Node.js 9.x
- Node.js 10.x
- Node.js 11.x
- Node.js 12.x
- Node.js 13.x
- Node.js 14.x
- Node.js 15.x
- Node.js 16.x
- Node.js 17.x
- Node.js 18.x
- Node.js 19.x
- Node.js 20.x
- Node.js 21.x
- Node.js 22.x
- Node.js 23.x
- Node.js 24.x
include:
- name: Node.js 0.8
node-version: "0.8"
npm-i: mocha@2.5.3 supertest@1.1.0
npm-rm: nyc
- name: Node.js 0.10
node-version: "0.10"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: Node.js 0.12
node-version: "0.12"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: io.js 1.x
node-version: "1"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: io.js 2.x
node-version: "2"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: io.js 3.x
node-version: "3"
npm-i: mocha@3.5.3 nyc@10.3.2 supertest@2.0.1
- name: Node.js 4.x
node-version: "4"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
- name: Node.js 5.x
node-version: "5"
npm-i: mocha@5.2.0 nyc@11.9.0 supertest@3.4.2
- name: Node.js 6.x
node-version: "6"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 7.x
node-version: "7"
npm-i: mocha@6.2.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 8.x
node-version: "8"
npm-i: mocha@7.1.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 9.x
node-version: "9"
npm-i: mocha@7.1.2 nyc@14.1.1 supertest@6.1.3
- name: Node.js 10.x
node-version: "10"
npm-i: mocha@8.4.0 supertest@6.1.3
- name: Node.js 11.x
node-version: "11"
npm-i: mocha@8.4.0 supertest@6.1.3
- name: Node.js 12.x
node-version: "12"
npm-i: mocha@9.2.2 supertest@6.1.3
- name: Node.js 13.x
node-version: "13"
npm-i: mocha@9.2.2 supertest@6.1.3
- name: Node.js 14.x
node-version: "14"
npm-i: supertest@6.1.3
- name: Node.js 15.x
node-version: "15"
npm-i: supertest@6.1.3
- name: Node.js 16.x
node-version: "16"
npm-i: supertest@6.1.3
- name: Node.js 17.x
node-version: "17"
npm-i: supertest@6.1.3
- name: Node.js 18.x
node-version: "18"
- name: Node.js 19.x
node-version: "19"
- name: Node.js 20.x
node-version: "20"
- name: Node.js 21.x
node-version: "21"
- name: Node.js 22.x
node-version: "22"
- name: Node.js 23.x
node-version: "23"
- name: Node.js 24.x
node-version: "24"
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Install Node.js ${{ matrix.node-version }}
shell: bash -eo pipefail -l {0}
run: |
nvm install --default ${{ matrix.node-version }}
if [[ "${{ matrix.node-version }}" == 0.* && "$(cut -d. -f2 <<< "${{ matrix.node-version }}")" -lt 10 ]]; then
nvm install --alias=npm 0.10
nvm use ${{ matrix.node-version }}
if [[ "$(npm -v)" == 1.1.* ]]; then
nvm exec npm npm install -g npm@1.1
ln -fs "$(which npm)" "$(dirname "$(nvm which npm)")/npm"
else
sed -i '1s;^.*$;'"$(printf '#!%q' "$(nvm which npm)")"';' "$(readlink -f "$(which npm)")"
fi
npm config set strict-ssl false
fi
dirname "$(nvm which ${{ matrix.node-version }})" >> "$GITHUB_PATH"
- name: Configure npm
run: |
if [[ "$(npm config get package-lock)" == "true" ]]; then
npm config set package-lock false
else
npm config set shrinkwrap false
fi
- name: Remove npm module(s) ${{ matrix.npm-rm }}
if: matrix.npm-rm != ''
run: npm rm --silent --save-dev ${{ matrix.npm-rm }}
- name: Install npm module(s) ${{ matrix.npm-i }}
if: matrix.npm-i != ''
run: npm install --save-dev ${{ matrix.npm-i }}
- name: Setup Node.js version-specific dependencies
shell: bash
run: |
# eslint for linting
# - remove on Node.js < 12
if [[ "$(cut -d. -f1 <<< "${{ matrix.node-version }}")" -lt 12 ]]; then
node -pe 'Object.keys(require("./package").devDependencies).join("\n")' | \
grep -E '^eslint(-|$)' | \
sort -r | \
xargs -n1 npm rm --silent --save-dev
fi
- name: Install Node.js dependencies
run: npm install
- name: List environment
id: list_env
shell: bash
run: |
echo "node@$(node -v)"
echo "npm@$(npm -v)"
npm -s ls ||:
(npm -s ls --depth=0 ||:) | awk -F'[ @]' 'NR>1 && $2 { print $2 "=" $3 }' >> "$GITHUB_OUTPUT"
- name: Run tests
shell: bash
run: |
if npm -ps ls nyc | grep -q nyc; then
npm run test-ci
else
npm test
fi
- name: Lint code
if: steps.list_env.outputs.eslint != ''
run: npm run lint
- name: Collect code coverage
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # master
if: steps.list_env.outputs.nyc != ''
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
flag-name: run-${{ matrix.test_number }}
parallel: true
coverage:
permissions:
checks: write # for coverallsapp/github-action to create new checks
needs: test
runs-on: ubuntu-latest
steps:
- name: Upload code coverage
uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b # master
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
parallel-finished: true
+78
View File
@@ -0,0 +1,78 @@
# For most projects, this workflow file will not need changing; you simply need
# to commit it to your repository.
#
# You may wish to alter this file to override the set of languages analyzed,
# or to provide custom queries or build logic.
#
# ******** NOTE ********
# We have attempted to detect the languages in your repository. Please check
# the `language` matrix defined below to confirm you have the correct set of
# supported CodeQL languages.
#
name: "CodeQL"
on:
push:
branches: ["master"]
pull_request:
# The branches below must be a subset of the branches above
branches: ["master"]
schedule:
- cron: "0 0 * * 1"
permissions:
contents: read
jobs:
analyze:
name: Analyze
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
language: ["javascript"]
# CodeQL supports [ $supported-codeql-languages ]
# Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support
steps:
- name: Harden the runner (Audit all outbound calls)
uses: step-security/harden-runner@ec9f2d5744a09debf3a187a3f4f675c53b671911 # v2.13.0
with:
egress-policy: audit
- name: Checkout repository
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
with:
languages: ${{ matrix.language }}
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
# ️ Command-line programs to run using the OS shell.
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
# If the Autobuild fails above, remove it and uncomment the following three lines.
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
# - run: |
# echo "Run, Build Application using script"
# ./location_of_script_within_repo/buildscript.sh
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
with:
category: "/language:${{matrix.language}}"
+73
View File
@@ -0,0 +1,73 @@
# This workflow uses actions that are not certified by GitHub. They are provided
# by a third-party and are governed by separate terms of service, privacy
# policy, and support documentation.
name: Scorecard supply-chain security
on:
# For Branch-Protection check. Only the default branch is supported. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
branch_protection_rule:
# To guarantee Maintained check is occasionally updated. See
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
schedule:
- cron: '16 21 * * 1'
push:
branches: [ "master" ]
# Declare default permissions as read only.
permissions: read-all
jobs:
analysis:
name: Scorecard analysis
runs-on: ubuntu-latest
permissions:
# Needed to upload the results to code-scanning dashboard.
security-events: write
# Needed to publish results and get a badge (see publish_results below).
id-token: write
# Uncomment the permissions below if installing in a private repository.
# contents: read
# actions: read
steps:
- name: "Checkout code"
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
uses: ossf/scorecard-action@05b42c624433fc40578a4040d5cf5e36ddca8cde # v2.4.2
with:
results_file: results.sarif
results_format: sarif
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
# - you want to enable the Branch-Protection check on a *public* repository, or
# - you are installing Scorecard on a *private* repository
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
# Public repositories:
# - Publish results to OpenSSF REST API for easy access by consumers
# - Allows the repository to include the Scorecard badge.
# - See https://github.com/ossf/scorecard-action#publishing-results.
# For private repositories:
# - `publish_results` will always be set to `false`, regardless
# of the value entered here.
publish_results: true
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
# format to the repository Actions tab.
- name: "Upload artifact"
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: SARIF file
path: results.sarif
retention-days: 5
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5
with:
sarif_file: results.sarif
+2
View File
@@ -1,3 +1,5 @@
.nyc_output/
coverage coverage
node_modules node_modules
npm-debug.log npm-debug.log
package-lock.json
-11
View File
@@ -1,11 +0,0 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.11"
matrix:
allow_failures:
- node_js: "0.11"
fast_finish: true
script: "npm run-script test-ci"
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"
+165
View File
@@ -1,3 +1,168 @@
unreleased
==========
* Fix icons for MIME types with `+`
* Fix text and json responses to match html sorting
* Show font icon for more font types
* Use 400 error on URI decode failure instead of 500
* deps: accepts@~1.3.7
- deps: mime-types@~2.1.24
- deps: negotiator@0.6.2
* deps: http-errors@~1.8.0
- Set constructor name when possible
- deps: depd@~1.1.2
- deps: inherits@2.0.4
- deps: setprototypeof@1.2.0
- deps: statuses@'>= 1.5.0 < 2'
* deps: mime-types@~2.1.30
- Add new mime types
- deps: mime-db@~1.47.0
* deps: parseurl@~1.3.3
1.9.1 / 2017-09-28
==================
* deps: accepts@~1.3.4
- deps: mime-types@~2.1.16
* deps: debug@2.6.9
* deps: http-errors@~1.6.2
- deps: depd@1.1.1
* deps: mime-types@~2.1.17
- Add new mime types
- deps: mime-db@~1.30.0
* deps: parseurl@~1.3.2
- perf: reduce overhead for full URLs
- perf: unroll the "fast-path" `RegExp`
1.9.0 / 2017-05-25
==================
* Set `X-Content-Type-Options: nosniff` header
* deps: batch@0.6.1
* deps: debug@2.6.8
- Allow colors in workers
- Deprecated `DEBUG_FD` environment variable set to `3` or higher
- Fix `DEBUG_MAX_ARRAY_LENGTH`
- Fix error when running under React Native
- Use same color for same namespace
- deps: ms@2.0.0
* deps: http-errors@~1.6.1
- Make `message` property enumerable for `HttpError`s
- deps: inherits@2.0.3
- deps: setprototypeof@1.0.3
- deps: statuses@'>= 1.3.1 < 2'
* deps: mime-types@~2.1.15
- Add new mime types
- Add `audio/mp3`
1.8.0 / 2016-06-17
==================
* Make inline file search case-insensitive
* deps: accepts@~1.3.3
- deps: mime-types@~2.1.11
- deps: negotiator@0.6.1
- perf: improve header parsing speed
* deps: http-errors@~1.5.0
- Use `setprototypeof` module to replace `__proto__` setting
- deps: inherits@2.0.1
- deps: statuses@'>= 1.3.0 < 2'
- perf: enable strict mode
* deps: mime-types@~2.1.11
- Add new mime types
- Update primary extension for `audio/mp4`
- deps: mime-db@~1.23.0
1.7.3 / 2016-01-24
==================
* deps: accepts@~1.2.13
- deps: mime-types@~2.1.6
* deps: batch@0.5.3
- Fix invalid dependency for browserify
* deps: escape-html@~1.0.3
- perf: enable strict mode
- perf: optimize string replacement
- perf: use faster string coercion
* deps: mime-types@~2.1.9
- Add new mime types
* deps: parseurl@~1.3.1
- perf: enable strict mode
1.7.2 / 2015-07-30
==================
* deps: accepts@~1.2.12
- deps: mime-types@~2.1.4
* deps: mime-types@~2.1.4
- Add new mime types
1.7.1 / 2015-07-05
==================
* deps: accepts@~1.2.10
- deps: mime-types@~2.1.2
* deps: mime-types@~2.1.2
- Add new mime types
1.7.0 / 2015-06-15
==================
* Accept `function` value for `template` option
* Send non-chunked response for `OPTIONS`
* Stat parent directory when necessary
* Use `Date.prototype.toLocaleDateString` to format date
* deps: accepts@~1.2.9
- deps: mime-types@~2.1.1
- deps: negotiator@0.5.3
- perf: avoid argument reassignment & argument slice
- perf: avoid negotiator recursive construction
- perf: enable strict mode
- perf: remove unnecessary bitwise operator
* deps: escape-html@1.0.2
* deps: mime-types@~2.1.1
- Add new mime types
* perf: enable strict mode
* perf: remove argument reassignment
1.6.4 / 2015-05-12
==================
* deps: accepts@~1.2.7
- deps: mime-types@~2.0.11
- deps: negotiator@0.5.3
* deps: debug@~2.2.0
- deps: ms@0.7.1
* deps: mime-types@~2.0.11
- Add new mime types
1.6.3 / 2015-03-13
==================
* Properly escape file names in HTML
* deps: accepts@~1.2.5
- deps: mime-types@~2.0.10
* deps: debug@~2.1.3
- Fix high intensity foreground color for bold
- deps: ms@0.7.0
* deps: escape-html@1.0.1
* deps: mime-types@~2.0.10
- Add new mime types
1.6.2 / 2015-02-16
==================
* deps: accepts@~1.2.4
- deps: mime-types@~2.0.9
- deps: negotiator@0.5.1
* deps: http-errors@~1.3.1
- Construct errors using defined constructors from `createError`
- Fix error names that are not identifiers
- Set a meaningful `name` property on constructed errors
* deps: mime-types@~2.0.9
- Add new mime types
- deps: mime-db@~1.7.0
1.6.1 / 2015-01-31 1.6.1 / 2015-01-31
================== ==================
+1 -1
View File
@@ -3,7 +3,7 @@
Copyright (c) 2010 Sencha Inc. Copyright (c) 2010 Sencha Inc.
Copyright (c) 2011 LearnBoost Copyright (c) 2011 LearnBoost
Copyright (c) 2011 TJ Holowaychuk Copyright (c) 2011 TJ Holowaychuk
Copyright (c) 2014 Douglas Christopher Wilson Copyright (c) 2014-2015 Douglas Christopher Wilson
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
+37 -16
View File
@@ -2,15 +2,18 @@
[![NPM Version][npm-image]][npm-url] [![NPM Version][npm-image]][npm-url]
[![NPM Downloads][downloads-image]][downloads-url] [![NPM Downloads][downloads-image]][downloads-url]
[![Linux Build][travis-image]][travis-url] [![Linux Build Status][ci-image]][ci-url]
[![Windows Build][appveyor-image]][appveyor-url] [![Windows Build][appveyor-image]][appveyor-url]
[![Test Coverage][coveralls-image]][coveralls-url] [![Coverage Status][coveralls-image]][coveralls-url]
[![Gratipay][gratipay-image]][gratipay-url]
Serves pages that contain directory listings for a given path. Serves pages that contain directory listings for a given path.
## Install ## Install
This is a [Node.js](https://nodejs.org/en/) module available through the
[npm registry](https://www.npmjs.com/). Installation is done using the
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
```sh ```sh
$ npm install serve-index $ npm install serve-index
``` ```
@@ -56,15 +59,31 @@ Optional path to a CSS stylesheet. Defaults to a built-in stylesheet.
##### template ##### template
Optional path to an HTML template. Defaults to a built-in template. Optional path to an HTML template or a function that will render a HTML
string. Defaults to a built-in template.
The following tokens are replaced in templates: When given a string, the string is used as a file path to load and then the
following tokens are replaced in templates:
* `{directory}` with the name of the directory. * `{directory}` with the name of the directory.
* `{files}` with the HTML of an unordered list of file links. * `{files}` with the HTML of an unordered list of file links.
* `{linked-path}` with the HTML of a link to the directory. * `{linked-path}` with the HTML of a link to the directory.
* `{style}` with the specified stylesheet and embedded images. * `{style}` with the specified stylesheet and embedded images.
When given as a function, the function is called as `template(locals, callback)`
and it needs to invoke `callback(error, htmlString)`. The following are the
provided locals:
* `directory` is the directory being displayed (where `/` is the root).
* `displayIcons` is a Boolean for if icons should be rendered or not.
* `fileList` is a sorted array of files in the directory. The array contains
objects with the following properties:
- `name` is the relative name for the file.
- `stat` is a `fs.Stats` object for the file.
* `path` is the full filesystem path to `directory`.
* `style` is the default stylesheet or the contents of the `stylesheet` option.
* `viewName` is the view name provided by the `view` option.
##### view ##### view
Display mode. `tiles` and `details` are available. Defaults to `tiles`. Display mode. `tiles` and `details` are available. Defaults to `tiles`.
@@ -107,8 +126,12 @@ var serveIndex = require('serve-index')
var app = express() var app = express()
// Serve URLs like /ftp/thing as public/ftp/thing // Serve URLs like /ftp/thing as public/ftp/thing
app.use('/ftp', serveIndex('public/ftp', {'icons': true})) // The express.static serves the file contents
app.listen() // The serveIndex is this module serving the directory
app.use('/ftp', express.static('public/ftp'), serveIndex('public/ftp', {'icons': true}))
// Listen
app.listen(3000)
``` ```
## License ## License
@@ -116,15 +139,13 @@ app.listen()
[MIT](LICENSE). The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons [MIT](LICENSE). The [Silk](http://www.famfamfam.com/lab/icons/silk/) icons
are created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/). are created by/copyright of [FAMFAMFAM](http://www.famfamfam.com/).
[npm-image]: https://img.shields.io/npm/v/serve-index.svg?style=flat [appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-index/master.svg?label=windows
[npm-url]: https://npmjs.org/package/serve-index
[travis-image]: https://img.shields.io/travis/expressjs/serve-index/master.svg?label=linux&style=flat
[travis-url]: https://travis-ci.org/expressjs/serve-index
[appveyor-image]: https://img.shields.io/appveyor/ci/dougwilson/serve-index/master.svg?label=windows&style=flat
[appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-index [appveyor-url]: https://ci.appveyor.com/project/dougwilson/serve-index
[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index/master.svg?style=flat [ci-image]: https://badgen.net/github/checks/expressjs/serve-index/master?label=ci
[ci-url]: https://github.com/expressjs/serve-index/actions/workflows/ci.yml
[coveralls-image]: https://img.shields.io/coveralls/expressjs/serve-index/master.svg
[coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master [coveralls-url]: https://coveralls.io/r/expressjs/serve-index?branch=master
[downloads-image]: https://img.shields.io/npm/dm/serve-index.svg?style=flat [downloads-image]: https://img.shields.io/npm/dm/serve-index.svg
[downloads-url]: https://npmjs.org/package/serve-index [downloads-url]: https://npmjs.org/package/serve-index
[gratipay-image]: https://img.shields.io/gratipay/dougwilson.svg?style=flat [npm-image]: https://img.shields.io/npm/v/serve-index.svg
[gratipay-url]: https://www.gratipay.com/dougwilson/ [npm-url]: https://npmjs.org/package/serve-index
+83 -9
View File
@@ -2,17 +2,91 @@ environment:
matrix: matrix:
- nodejs_version: "0.8" - nodejs_version: "0.8"
- nodejs_version: "0.10" - nodejs_version: "0.10"
- nodejs_version: "0.11" - nodejs_version: "0.12"
matrix: - nodejs_version: "1.8"
allow_failures: - nodejs_version: "2.5"
- nodejs_version: "0.11" - nodejs_version: "3.3"
fast_finish: true - nodejs_version: "4.9"
- nodejs_version: "5.12"
- nodejs_version: "6.17"
- nodejs_version: "7.10"
- nodejs_version: "8.17"
- nodejs_version: "9.11"
- nodejs_version: "10.24"
- nodejs_version: "11.15"
- nodejs_version: "12.22"
- nodejs_version: "13.14"
- nodejs_version: "14.16"
- nodejs_version: "15.13"
cache:
- node_modules
install: install:
- ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) # Install Node.js
- ps: >-
try { Install-Product node $env:nodejs_version -ErrorAction Stop }
catch { Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) }
# Configure npm
- ps: |
# Skip updating shrinkwrap / lock
npm config set shrinkwrap false
# Skip SSL validation on Node.js < 0.10
if ([int]$env:nodejs_version.split(".")[0] -eq 0 -and [int]$env:nodejs_version.split(".")[1] -lt 10) {
npm config set strict-ssl false
}
# Remove all non-test dependencies
- ps: |
# Remove coverage dependency
npm rm --silent --save-dev nyc
# Remove lint dependencies
cmd.exe /c "node -pe `"Object.keys(require('./package').devDependencies).join('\n')`"" | `
sls "^eslint(-|$)" | `
%{ npm rm --silent --save-dev $_ }
# Setup Node.js version-specific dependencies
- ps: |
# mocha for testing
# - use 2.x for Node.js < 0.10
# - use 3.x for Node.js < 4
# - use 5.x for Node.js < 6
# - use 6.x for Node.js < 8
# - use 7.x for Node.js < 10
if ([int]$env:nodejs_version.split(".")[0] -eq 0 -and [int]$env:nodejs_version.split(".")[1] -lt 10) {
npm install --silent --save-dev mocha@2.5.3
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev mocha@3.5.3
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev mocha@5.2.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 8) {
npm install --silent --save-dev mocha@6.2.2
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 10) {
npm install --silent --save-dev mocha@7.2.0
}
- ps: |
# mocha for testing
# - use 1.1.0 for Node.js < 0.10
# - use 2.0.0 for Node.js < 4
# - use 3.4.2 for Node.js < 6
if ([int]$env:nodejs_version.split(".")[0] -eq 0 -and [int]$env:nodejs_version.split(".")[1] -lt 10) {
npm install --silent --save-dev supertest@1.1.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 4) {
npm install --silent --save-dev supertest@2.0.0
} elseif ([int]$env:nodejs_version.split(".")[0] -lt 6) {
npm install --silent --save-dev supertest@3.4.2
}
# Update Node.js modules
- ps: |
# Prune & rebuild node_modules
if (Test-Path -Path node_modules) {
npm prune
npm rebuild
}
# Install Node.js modules
- npm install - npm install
build: off build: off
test_script: test_script:
- node --version # Output version data
- npm --version - ps: |
- npm run test-ci node --version
npm --version
# Run test script
- npm test
version: "{build}" version: "{build}"
+248 -131
View File
@@ -2,20 +2,21 @@
* serve-index * serve-index
* Copyright(c) 2011 Sencha Inc. * Copyright(c) 2011 Sencha Inc.
* Copyright(c) 2011 TJ Holowaychuk * Copyright(c) 2011 TJ Holowaychuk
* Copyright(c) 2014 Douglas Christopher Wilson * Copyright(c) 2014-2015 Douglas Christopher Wilson
* MIT Licensed * MIT Licensed
*/ */
// TODO: arrow key navigation 'use strict';
// TODO: make icons extensible
/** /**
* Module dependencies. * Module dependencies.
* @private
*/ */
var accepts = require('accepts'); var accepts = require('accepts');
var createError = require('http-errors'); var createError = require('http-errors');
var debug = require('debug')('serve-index'); var debug = require('debug')('serve-index');
var escapeHtml = require('escape-html');
var fs = require('fs') var fs = require('fs')
, path = require('path') , path = require('path')
, normalize = path.normalize , normalize = path.normalize
@@ -27,6 +28,13 @@ var mime = require('mime-types');
var parseUrl = require('parseurl'); var parseUrl = require('parseurl');
var resolve = require('path').resolve; var resolve = require('path').resolve;
/**
* Module exports.
* @public
*/
module.exports = serveIndex;
/*! /*!
* Icon cache. * Icon cache.
*/ */
@@ -66,59 +74,63 @@ var mediaType = {
* *
* See Readme.md for documentation of options. * See Readme.md for documentation of options.
* *
* @param {String} path * @param {String} root
* @param {Object} options * @param {Object} options
* @return {Function} middleware * @return {Function} middleware
* @api public * @public
*/ */
exports = module.exports = function serveIndex(root, options){ function serveIndex(root, options) {
options = options || {}; var opts = options || {};
// root required // root required
if (!root) throw new TypeError('serveIndex() root path required'); if (!root) {
throw new TypeError('serveIndex() root path required');
}
// resolve root to absolute and normalize // resolve root to absolute and normalize
root = resolve(root); var rootPath = normalize(resolve(root) + sep);
root = normalize(root + sep);
var hidden = options.hidden var filter = opts.filter;
, icons = options.icons var hidden = opts.hidden;
, view = options.view || 'tiles' var icons = opts.icons;
, filter = options.filter var stylesheet = opts.stylesheet || defaultStylesheet;
, template = options.template || defaultTemplate var template = opts.template || defaultTemplate;
, stylesheet = options.stylesheet || defaultStylesheet; var view = opts.view || 'tiles';
return function serveIndex(req, res, next) { return function (req, res, next) {
if (req.method !== 'GET' && req.method !== 'HEAD') { if (req.method !== 'GET' && req.method !== 'HEAD') {
res.statusCode = 'OPTIONS' === req.method res.statusCode = 'OPTIONS' === req.method ? 200 : 405;
? 200
: 405;
res.setHeader('Allow', 'GET, HEAD, OPTIONS'); res.setHeader('Allow', 'GET, HEAD, OPTIONS');
res.setHeader('Content-Length', '0');
res.end(); res.end();
return; return;
} }
// get dir
var dir = getRequestedDir(req)
// bad request
if (dir === null) return next(createError(400))
// parse URLs // parse URLs
var url = parseUrl(req);
var originalUrl = parseUrl.original(req); var originalUrl = parseUrl.original(req);
var dir = decodeURIComponent(url.pathname);
var originalDir = decodeURIComponent(originalUrl.pathname); var originalDir = decodeURIComponent(originalUrl.pathname);
// join / normalize from root dir // join / normalize from root dir
var path = normalize(join(root, dir)); var path = normalize(join(rootPath, dir));
// null byte(s), bad request // null byte(s), bad request
if (~path.indexOf('\0')) return next(createError(400)); if (~path.indexOf('\0')) return next(createError(400));
// malicious path // malicious path
if ((path + sep).substr(0, root.length) !== root) { if ((path + sep).substr(0, rootPath.length) !== rootPath) {
debug('malicious path "%s"', path); debug('malicious path "%s"', path);
return next(createError(403)); return next(createError(403));
} }
// determine ".." display // determine ".." display
var showUp = normalize(resolve(path) + sep) !== root; var showUp = normalize(resolve(path) + sep) !== rootPath;
// check if we have a directory // check if we have a directory
debug('stat "%s"', path); debug('stat "%s"', path);
@@ -152,7 +164,7 @@ exports = module.exports = function serveIndex(root, options){
// not acceptable // not acceptable
if (!type) return next(createError(406)); if (!type) return next(createError(406));
exports[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet); serveIndex[mediaType[type]](req, res, files, next, originalDir, showUp, icons, path, view, template, stylesheet);
}); });
}); });
}; };
@@ -162,26 +174,40 @@ exports = module.exports = function serveIndex(root, options){
* Respond with text/html. * Respond with text/html.
*/ */
exports.html = function(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet){ serveIndex.html = function _html(req, res, files, next, dir, showUp, icons, path, view, template, stylesheet) {
fs.readFile(template, 'utf8', function(err, str){ var render = typeof template !== 'function'
if (err) return next(err); ? createHtmlRender(template)
fs.readFile(stylesheet, 'utf8', function(err, style){ : template
if (err) return next(err);
stat(path, files, function(err, stats){
if (err) return next(err);
files = files.map(function(file, i){ return { name: file, stat: stats[i] }; });
files.sort(fileSort);
if (showUp) files.unshift({ name: '..' });
str = str
.replace(/\{style\}/g, style.concat(iconStyle(files, icons)))
.replace(/\{files\}/g, html(files, dir, icons, view))
.replace(/\{directory\}/g, dir)
.replace(/\{linked-path\}/g, htmlPath(dir));
var buf = new Buffer(str, 'utf8'); if (showUp) {
res.setHeader('Content-Type', 'text/html; charset=utf-8'); files.unshift('..');
res.setHeader('Content-Length', buf.length); }
res.end(buf);
// stat all files
stat(path, files, function (err, fileList) {
if (err) return next(err);
// sort file list
fileList.sort(fileSort);
// read stylesheet
fs.readFile(stylesheet, 'utf8', function (err, style) {
if (err) return next(err);
// create locals for rendering
var locals = {
directory: dir,
displayIcons: Boolean(icons),
fileList: fileList,
path: path,
style: style,
viewName: view
};
// render html
render(locals, function (err, body) {
if (err) return next(err);
send(res, 'text/html', body)
}); });
}); });
}); });
@@ -191,47 +217,175 @@ exports.html = function(req, res, files, next, dir, showUp, icons, path, view, t
* Respond with application/json. * Respond with application/json.
*/ */
exports.json = function(req, res, files){ serveIndex.json = function _json (req, res, files, next, dir, showUp, icons, path) {
var body = JSON.stringify(files); // stat all files
var buf = new Buffer(body, 'utf8'); stat(path, files, function (err, fileList) {
if (err) return next(err)
res.setHeader('Content-Type', 'application/json; charset=utf-8'); // sort file list
res.setHeader('Content-Length', buf.length); fileList.sort(fileSort)
res.end(buf);
// serialize
var body = JSON.stringify(fileList.map(function (file) {
return file.name
}))
send(res, 'application/json', body)
})
}; };
/** /**
* Respond with text/plain. * Respond with text/plain.
*/ */
exports.plain = function(req, res, files){ serveIndex.plain = function _plain (req, res, files, next, dir, showUp, icons, path) {
var body = files.join('\n') + '\n'; // stat all files
var buf = new Buffer(body, 'utf8'); stat(path, files, function (err, fileList) {
if (err) return next(err)
res.setHeader('Content-Type', 'text/plain; charset=utf-8'); // sort file list
res.setHeader('Content-Length', buf.length); fileList.sort(fileSort)
res.end(buf);
// serialize
var body = fileList.map(function (file) {
return file.name
}).join('\n') + '\n'
send(res, 'text/plain', body)
})
}; };
/**
* Map html `files`, returning an html unordered list.
* @private
*/
function createHtmlFileList(files, dir, useIcons, view) {
var html = '<ul id="files" class="view-' + escapeHtml(view) + '">'
+ (view === 'details' ? (
'<li class="header">'
+ '<span class="name">Name</span>'
+ '<span class="size">Size</span>'
+ '<span class="date">Modified</span>'
+ '</li>') : '');
html += files.map(function (file) {
var classes = [];
var isDir = file.stat && file.stat.isDirectory();
var path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
if (useIcons) {
classes.push('icon');
if (isDir) {
classes.push('icon-directory');
} else {
var ext = extname(file.name);
var icon = iconLookup(file.name);
classes.push('icon');
classes.push('icon-' + ext.substring(1));
if (classes.indexOf(icon.className) === -1) {
classes.push(icon.className);
}
}
}
path.push(encodeURIComponent(file.name));
var date = file.stat && file.name !== '..'
? file.stat.mtime.toLocaleDateString() + ' ' + file.stat.mtime.toLocaleTimeString()
: '';
var size = file.stat && !isDir
? file.stat.size
: '';
return '<li><a href="'
+ escapeHtml(normalizeSlashes(normalize(path.join('/'))))
+ '" class="' + escapeHtml(classes.join(' ')) + '"'
+ ' title="' + escapeHtml(file.name) + '">'
+ '<span class="name">' + escapeHtml(file.name) + '</span>'
+ '<span class="size">' + escapeHtml(size) + '</span>'
+ '<span class="date">' + escapeHtml(date) + '</span>'
+ '</a></li>';
}).join('\n');
html += '</ul>';
return html;
}
/**
* Create function to render html.
*/
function createHtmlRender(template) {
return function render(locals, callback) {
// read template
fs.readFile(template, 'utf8', function (err, str) {
if (err) return callback(err);
var body = str
.replace(/\{style\}/g, locals.style.concat(iconStyle(locals.fileList, locals.displayIcons)))
.replace(/\{files\}/g, createHtmlFileList(locals.fileList, locals.directory, locals.displayIcons, locals.viewName))
.replace(/\{directory\}/g, escapeHtml(locals.directory))
.replace(/\{linked-path\}/g, htmlPath(locals.directory));
callback(null, body);
});
};
}
/** /**
* Sort function for with directories first. * Sort function for with directories first.
*/ */
function fileSort(a, b) { function fileSort(a, b) {
// sort ".." to the top
if (a.name === '..' || b.name === '..') {
return a.name === b.name ? 0
: a.name === '..' ? -1 : 1;
}
return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) || return Number(b.stat && b.stat.isDirectory()) - Number(a.stat && a.stat.isDirectory()) ||
String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase()); String(a.name).toLocaleLowerCase().localeCompare(String(b.name).toLocaleLowerCase());
} }
/**
* Get the requested directory from request.
*
* @param req
* @return {string}
* @api private
*/
function getRequestedDir (req) {
try {
return decodeURIComponent(parseUrl(req).pathname)
} catch (e) {
return null
}
}
/** /**
* Map html `dir`, returning a linked path. * Map html `dir`, returning a linked path.
*/ */
function htmlPath(dir) { function htmlPath(dir) {
var curr = []; var parts = dir.split('/');
return dir.split('/').map(function(part){ var crumb = new Array(parts.length);
curr.push(encodeURIComponent(part));
return part ? '<a href="' + curr.join('/') + '">' + part + '</a>' : ''; for (var i = 0; i < parts.length; i++) {
}).join(' / '); var part = parts[i];
if (part) {
parts[i] = encodeURIComponent(part);
crumb[i] = '<a href="' + escapeHtml(parts.slice(0, i + 1).join('/')) + '">' + escapeHtml(part) + '</a>';
}
}
return crumb.join(' / ');
} }
/** /**
@@ -262,7 +416,7 @@ function iconLookup(filename) {
// try by mime type // try by mime type
if (icons[mimetype]) { if (icons[mimetype]) {
return { return {
className: 'icon-' + mimetype.replace('/', '-'), className: 'icon-' + mimetype.replace('/', '-').replace('+', '_'),
fileName: icons[mimetype] fileName: icons[mimetype]
}; };
} }
@@ -296,11 +450,9 @@ function iconLookup(filename) {
* Load icon images, return css string. * Load icon images, return css string.
*/ */
function iconStyle (files, useIcons) { function iconStyle(files, useIcons) {
if (!useIcons) return ''; if (!useIcons) return '';
var className;
var i; var i;
var iconName;
var list = []; var list = [];
var rules = {}; var rules = {};
var selector; var selector;
@@ -310,7 +462,7 @@ function iconStyle (files, useIcons) {
for (i = 0; i < files.length; i++) { for (i = 0; i < files.length; i++) {
var file = files[i]; var file = files[i];
var isDir = '..' == file.name || (file.stat && file.stat.isDirectory()); var isDir = file.stat && file.stat.isDirectory();
var icon = isDir var icon = isDir
? { className: 'icon-directory', fileName: icons.folder } ? { className: 'icon-directory', fileName: icons.folder }
: iconLookup(file.name); : iconLookup(file.name);
@@ -337,63 +489,6 @@ function iconStyle (files, useIcons) {
return style; return style;
} }
/**
* Map html `files`, returning an html unordered list.
*/
function html(files, dir, useIcons, view) {
return '<ul id="files" class="view-' + view + '">'
+ (view == 'details' ? (
'<li class="header">'
+ '<span class="name">Name</span>'
+ '<span class="size">Size</span>'
+ '<span class="date">Modified</span>'
+ '</li>') : '')
+ files.map(function(file){
var isDir = '..' == file.name || (file.stat && file.stat.isDirectory())
, classes = []
, path = dir.split('/').map(function (c) { return encodeURIComponent(c); });
if (useIcons) {
classes.push('icon');
if (isDir) {
classes.push('icon-directory');
} else {
var ext = extname(file.name);
var icon = iconLookup(file.name);
classes.push('icon');
classes.push('icon-' + ext.substring(1));
if (classes.indexOf(icon.className) === -1) {
classes.push(icon.className);
}
}
}
path.push(encodeURIComponent(file.name));
var date = file.stat && file.name !== '..'
? file.stat.mtime.toDateString() + ' ' + file.stat.mtime.toLocaleTimeString()
: '';
var size = file.stat && !isDir
? file.stat.size
: '';
return '<li><a href="'
+ normalizeSlashes(normalize(path.join('/')))
+ '" class="'
+ classes.join(' ') + '"'
+ ' title="' + file.name + '">'
+ '<span class="name">'+file.name+'</span>'
+ '<span class="size">'+size+'</span>'
+ '<span class="date">'+date+'</span>'
+ '</a></li>';
}).join('\n') + '</ul>';
}
/** /**
* Load and cache the given `icon`. * Load and cache the given `icon`.
* *
@@ -431,13 +526,34 @@ function normalizeSlashes(path) {
function removeHidden(files) { function removeHidden(files) {
return files.filter(function(file){ return files.filter(function(file){
return '.' != file[0]; return file[0] !== '.'
}); });
} }
/** /**
* Stat all files and return array of stat * Send a response.
* in same order. * @private
*/
function send (res, type, body) {
// security header for content sniffing
res.setHeader('X-Content-Type-Options', 'nosniff')
// standard headers
res.setHeader('Content-Type', type + '; charset=utf-8')
res.setHeader('Content-Length', Buffer.byteLength(body, 'utf8'))
// body
res.end(body, 'utf8')
}
/**
* Stat all files and return array of objects in the form
* `{ name, stat }`.
*
* @param {Array} files
* @return {Array}
* @api private
*/ */
function stat(dir, files, cb) { function stat(dir, files, cb) {
@@ -451,7 +567,10 @@ function stat(dir, files, cb) {
if (err && err.code !== 'ENOENT') return done(err); if (err && err.code !== 'ENOENT') return done(err);
// pass ENOENT as null stat, not error // pass ENOENT as null stat, not error
done(null, stat || null); done(null, {
name: file,
stat: stat || null
})
}); });
}); });
}); });
@@ -469,6 +588,7 @@ var icons = {
'folder': 'folder.png', 'folder': 'folder.png',
// generic mime type icons // generic mime type icons
'font': 'font.png',
'image': 'image.png', 'image': 'image.png',
'text': 'page_white_text.png', 'text': 'page_white_text.png',
'video': 'film.png', 'video': 'film.png',
@@ -479,7 +599,6 @@ var icons = {
'+zip': 'box.png', '+zip': 'box.png',
// specific mime type icons // specific mime type icons
'application/font-woff': 'font.png',
'application/javascript': 'page_white_code_red.png', 'application/javascript': 'page_white_code_red.png',
'application/json': 'page_white_code.png', 'application/json': 'page_white_code.png',
'application/msword': 'page_white_word.png', 'application/msword': 'page_white_word.png',
@@ -493,7 +612,6 @@ var icons = {
'application/vnd.oasis.opendocument.text': 'page_white_word.png', 'application/vnd.oasis.opendocument.text': 'page_white_word.png',
'application/x-7z-compressed': 'box.png', 'application/x-7z-compressed': 'box.png',
'application/x-sh': 'application_xp_terminal.png', 'application/x-sh': 'application_xp_terminal.png',
'application/x-font-ttf': 'font.png',
'application/x-msaccess': 'page_white_database.png', 'application/x-msaccess': 'page_white_database.png',
'application/x-shockwave-flash': 'page_white_flash.png', 'application/x-shockwave-flash': 'page_white_flash.png',
'application/x-sql': 'page_white_database.png', 'application/x-sql': 'page_white_database.png',
@@ -547,7 +665,6 @@ var icons = {
'.map': 'map.png', '.map': 'map.png',
'.msi': 'box.png', '.msi': 'box.png',
'.mv4': 'film.png', '.mv4': 'film.png',
'.otf': 'font.png',
'.pdb': 'page_white_database.png', '.pdb': 'page_white_database.png',
'.php': 'page_white_php.png', '.php': 'page_white_php.png',
'.pl': 'page_white_code.png', '.pl': 'page_white_code.png',
+21 -13
View File
@@ -1,23 +1,30 @@
{ {
"name": "serve-index", "name": "serve-index",
"description": "Serve directory listings", "description": "Serve directory listings",
"version": "1.6.1", "version": "1.9.1",
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>", "author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
"license": "MIT", "license": "MIT",
"repository": "expressjs/serve-index", "repository": "expressjs/serve-index",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/express"
},
"dependencies": { "dependencies": {
"accepts": "~1.2.3", "accepts": "~1.3.7",
"batch": "0.5.2", "batch": "0.6.1",
"debug": "~2.1.1", "debug": "2.6.9",
"http-errors": "~1.2.8", "escape-html": "~1.0.3",
"mime-types": "~2.0.8", "http-errors": "~1.8.0",
"parseurl": "~1.3.0" "mime-types": "~2.1.29",
"parseurl": "~1.3.3"
}, },
"devDependencies": { "devDependencies": {
"after": "0.8.1", "after": "0.8.2",
"istanbul": "0.3.5", "eslint": "7.23.0",
"mocha": "~2.1.0", "eslint-plugin-markdown": "2.0.0",
"supertest": "~0.15.0" "mocha": "10.4.0",
"nyc": "15.1.0",
"supertest": "7.0.0"
}, },
"files": [ "files": [
"public/", "public/",
@@ -29,8 +36,9 @@
"node": ">= 0.8.0" "node": ">= 0.8.0"
}, },
"scripts": { "scripts": {
"lint": "eslint .",
"test": "mocha --reporter spec --bail --check-leaks test/", "test": "mocha --reporter spec --bail --check-leaks test/",
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/", "test-ci": "nyc --reporter=lcov --reporter=text npm test",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/" "test-cov": "nyc --reporter=html --reporter=text npm test"
} }
} }
+3 -3
View File
@@ -52,11 +52,11 @@
} }
function search() { function search() {
var str = $('search').value var str = $('search').value.toLowerCase();
, links = $('files').all('a'); var links = $('files').all('a');
links.each(function(link){ links.each(function(link){
var text = link.textContent; var text = link.textContent.toLowerCase();
if ('..' == text) return; if ('..' == text) return;
if (str.length && ~text.indexOf(str)) { if (str.length && ~text.indexOf(str)) {
View File
View File
+1
View File
@@ -0,0 +1 @@
ferret
-26
View File
@@ -1,26 +0,0 @@
var bytes = require('bytes');
exports['default request body'] = function(app){
it('should default to {}', function(done){
app.request()
.post('/')
.end(function(res){
res.body.should.equal('{}');
done();
})
})
};
exports['limit body to'] = function(size, type, app){
it('should accept a limit option', function(done){
app.request()
.post('/')
.set('Content-Length', bytes(size) + 1)
.set('Content-Type', type)
.end(function(res){
res.should.have.status(413);
done();
})
})
}
+313 -72
View File
@@ -26,6 +26,15 @@ describe('serveIndex(root)', function () {
.expect(200, done) .expect(200, done)
}) })
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should serve a directory index', function (done) { it('should serve a directory index', function (done) {
var server = createServer() var server = createServer()
@@ -39,7 +48,9 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.head('/') .head('/')
.expect(200, '', done) .expect(200)
.expect(shouldNotHaveBody())
.end(done)
}) })
it('should work with OPTIONS requests', function (done) { it('should work with OPTIONS requests', function (done) {
@@ -67,6 +78,14 @@ describe('serveIndex(root)', function () {
.expect(400, done) .expect(400, done)
}) })
it('should deny path that does not decode', function (done) {
var server = createServer()
request(server)
.head('/%FF')
.expect(400, done)
})
it('should deny path outside root', function (done) { it('should deny path outside root', function (done) {
var server = createServer() var server = createServer()
@@ -84,11 +103,11 @@ describe('serveIndex(root)', function () {
}) })
it('should treat an ENAMETOOLONG as a 414', function (done) { it('should treat an ENAMETOOLONG as a 414', function (done) {
var path = Array(11000).join('foobar') var dir = path.join(fixtures, Array(10000).join('/foobar'))
var server = createServer() var server = createServer(dir)
request(server) request(server)
.get('/' + path) .get('/')
.expect(414, done) .expect(414, done)
}) })
@@ -109,7 +128,7 @@ describe('serveIndex(root)', function () {
.get('/') .get('/')
.set('Accept', 'application/json') .set('Accept', 'application/json')
.expect('Content-Type', /json/) .expect('Content-Type', /json/)
.expect(/g# %3 o %2525 %37 dir/) .expect(/g# %3 o & %2525 %37 dir/)
.expect(/users/) .expect(/users/)
.expect(/file #1\.txt/) .expect(/file #1\.txt/)
.expect(/nums/) .expect(/nums/)
@@ -117,6 +136,36 @@ describe('serveIndex(root)', function () {
.expect(/さくら\.txt/) .expect(/さくら\.txt/)
.expect(200, done) .expect(200, done)
}); });
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'application/json')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should sort folders first', function (done) {
request(createServer())
.get('/')
.set('Accept', 'application/json')
.expect(200)
.expect('Content-Type', 'application/json; charset=utf-8')
.expect([
'#directory',
'collect',
'g# %3 o & %2525 %37 dir',
'users',
'file #1.txt',
'foo & bar',
'nums',
'todo.txt',
'さくら.txt'
])
.end(done)
})
}); });
describe('when Accept: text/html is given', function () { describe('when Accept: text/html is given', function () {
@@ -128,12 +177,36 @@ describe('serveIndex(root)', function () {
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/) .expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/<a href="\/users"/) .expect(/<a href="\/users"/)
.expect(/<a href="\/file%20%231.txt"/) .expect(/<a href="\/file%20%231.txt"/)
.expect(/<a href="\/todo.txt"/) .expect(/<a href="\/todo.txt"/)
.expect(/<a href="\/%E3%81%95%E3%81%8F%E3%82%89\.txt"/) .expect(/<a href="\/%E3%81%95%E3%81%8F%E3%82%89\.txt"/)
.end(done); .end(done)
});
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'text/html')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should property escape file names', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200)
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/foo%20%26%20bar"/)
.expect(/foo &amp; bar/)
.expect(bodyDoesNotContain('foo & bar'))
.end(done)
}); });
it('should sort folders first', function (done) { it('should sort folders first', function (done) {
@@ -144,23 +217,23 @@ describe('serveIndex(root)', function () {
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.end(function (err, res) { .expect(function (res) {
if (err) done(err); var urls = res.text
var body = res.text.split('</h1>')[1]; .split('</h1>')[1]
var urls = body.split(/<a href="([^"]*)"/).filter(function(s, i){ return i%2; }); .split(/<a href="([^"]*)"/).filter(function (s, i) { return i % 2 })
assert.deepEqual(urls, [ assert.deepEqual(urls, [
'/%23directory', '/%23directory',
'/collect', '/collect',
'/g%23%20%253%20o%20%252525%20%2537%20dir', '/g%23%20%253%20o%20%26%20%252525%20%2537%20dir',
'/users', '/users',
'/file%20%231.txt', '/file%20%231.txt',
'/foo%20bar', '/foo%20%26%20bar',
'/nums', '/nums',
'/todo.txt', '/todo.txt',
'/%E3%81%95%E3%81%8F%E3%82%89.txt' '/%E3%81%95%E3%81%8F%E3%82%89.txt'
]); ])
done(); })
}); .end(done)
}); });
}); });
@@ -174,12 +247,43 @@ describe('serveIndex(root)', function () {
.expect(200) .expect(200)
.expect('Content-Type', 'text/plain; charset=utf-8') .expect('Content-Type', 'text/plain; charset=utf-8')
.expect(/users/) .expect(/users/)
.expect(/g# %3 o %2525 %37 dir/) .expect(/g# %3 o & %2525 %37 dir/)
.expect(/file #1.txt/) .expect(/file #1.txt/)
.expect(/todo.txt/) .expect(/todo.txt/)
.expect(/さくら\.txt/) .expect(/さくら\.txt/)
.end(done); .end(done)
}); });
it('should include security header', function (done) {
var server = createServer()
request(server)
.get('/')
.set('Accept', 'text/plain')
.expect('X-Content-Type-Options', 'nosniff')
.expect(200, done)
})
it('should sort folders first', function (done) {
request(createServer())
.get('/')
.set('Accept', 'text/plain')
.expect(200)
.expect('Content-Type', 'text/plain; charset=utf-8')
.expect([
'#directory',
'collect',
'g# %3 o & %2525 %37 dir',
'users',
'file #1.txt',
'foo & bar',
'nums',
'todo.txt',
'さくら.txt',
''
].join('\n'))
.end(done)
})
}); });
describe('when Accept: application/x-bogus is given', function () { describe('when Accept: application/x-bogus is given', function () {
@@ -256,7 +360,7 @@ describe('serveIndex(root)', function () {
}); });
it('should filter directory paths', function (done) { it('should filter directory paths', function (done) {
var cb = after(3, done) var cb = after(4, done)
var server = createServer(fixtures, {'filter': filter}) var server = createServer(fixtures, {'filter': filter})
function filter(name, index, list, dir) { function filter(name, index, list, dir) {
@@ -282,6 +386,7 @@ describe('serveIndex(root)', function () {
.expect(/icon-default/) .expect(/icon-default/)
.expect(/icon-directory/) .expect(/icon-directory/)
.expect(/icon-image/) .expect(/icon-image/)
.expect(/icon-image-svg_xml/)
.expect(/icon-txt/) .expect(/icon-txt/)
.expect(/icon-application-pdf/) .expect(/icon-application-pdf/)
.expect(/icon-video/) .expect(/icon-video/)
@@ -290,6 +395,146 @@ describe('serveIndex(root)', function () {
}); });
}); });
describe('with "template" option', function () {
describe('when setting a custom template file', function () {
var server;
before(function () {
server = createServer(fixtures, {'template': __dirname + '/shared/template.html'});
});
it('should respond with file list', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/<a href="\/users"/)
.expect(/<a href="\/file%20%231.txt"/)
.expect(/<a href="\/todo.txt"/)
.expect(200, done)
});
it('should respond with testing template sentence', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /This is the test template/, done)
});
it('should have default styles', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /ul#files/, done)
});
it('should list directory twice', function (done) {
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(function (res) {
var occurances = res.text.match(/directory \/users\//g)
if (occurances && occurances.length === 2) return
throw new Error('directory not listed twice')
})
.expect(200, done)
});
});
describe('when setting a custom template function', function () {
it('should invoke function to render', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, 'This is a template.');
}});
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, 'This is a template.', done)
});
it('should handle render errors', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(new Error('boom!'));
}});
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(500, 'boom!', done)
});
it('should provide "directory" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.directory));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, '"/users/"', done)
});
it('should provide "displayIcons" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.displayIcons));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, 'false', done)
});
it('should provide "fileList" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.fileList.map(function (file) {
file.stat = file.stat instanceof fs.Stats;
return file;
})));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect('[{"name":"..","stat":true},{"name":"#dir","stat":true},{"name":"index.html","stat":true},{"name":"tobi.txt","stat":true}]')
.expect(200, done)
});
it('should provide "path" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.path));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, JSON.stringify(path.join(fixtures, 'users/')), done)
});
it('should provide "style" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.style));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, /#files \.icon \.name/, done)
});
it('should provide "viewName" local', function (done) {
var server = createServer(fixtures, {'template': function (locals, callback) {
callback(null, JSON.stringify(locals.viewName));
}});
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(200, '"tiles"', done)
});
});
});
describe('when using custom handler', function () { describe('when using custom handler', function () {
describe('exports.html', function () { describe('exports.html', function () {
alterProperty(serveIndex, 'html', serveIndex.html) alterProperty(serveIndex, 'html', serveIndex.html)
@@ -434,7 +679,27 @@ describe('serveIndex(root)', function () {
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/users\/index.html"/) .expect(/<a href="\/users\/index.html"/)
.expect(/<a href="\/users\/tobi.txt"/) .expect(/<a href="\/users\/tobi.txt"/)
.end(done); .end(done)
});
it('should include link to parent directory', function (done) {
var server = createServer()
request(server)
.get('/users')
.expect(200)
.expect(function (res) {
var urls = res.text
.split('</h1>')[1]
.split(/<a href="([^"]*)"/).filter(function (s, i) { return i % 2 })
assert.deepEqual(urls, [
'/',
'/users/%23dir',
'/users/index.html',
'/users/tobi.txt'
])
})
.end(done)
}); });
it('should work for directory with #', function (done) { it('should work for directory with #', function (done) {
@@ -447,20 +712,34 @@ describe('serveIndex(root)', function () {
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/%23directory"/) .expect(/<a href="\/%23directory"/)
.expect(/<a href="\/%23directory\/index.html"/) .expect(/<a href="\/%23directory\/index.html"/)
.end(done); .end(done)
}); });
it('should work for directory with special chars', function (done) { it('should work for directory with special chars', function (done) {
var server = createServer() var server = createServer()
request(server) request(server)
.get('/g%23%20%253%20o%20%252525%20%2537%20dir/') .get('/g%23%20%253%20o%20%26%20%252525%20%2537%20dir/')
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/) .expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir\/empty.txt"/) .expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir\/empty.txt"/)
.end(done); .end(done)
});
it('should property escape directory names', function (done) {
var server = createServer()
request(server)
.get('/g%23%20%253%20o%20%26%20%252525%20%2537%20dir/')
.set('Accept', 'text/html')
.expect(200)
.expect('Content-Type', 'text/html; charset=utf-8')
.expect(/<a href="\/g%23%20%253%20o%20%26%20%252525%20%2537%20dir"/)
.expect(/g# %3 o &amp; %2525 %37 dir/)
.expect(bodyDoesNotContain('g# %3 o & %2525 %37 dir'))
.end(done)
}); });
it('should not work for outside root', function (done) { it('should not work for outside root', function (done) {
@@ -469,51 +748,7 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.get('/../support/') .get('/../support/')
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(403, done); .expect(403, done)
});
});
describe('when setting a custom template', function () {
var server;
before(function () {
server = createServer(fixtures, {'template': __dirname + '/shared/template.html'});
});
it('should respond with file list', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(/<a href="\/g%23%20%253%20o%20%252525%20%2537%20dir"/)
.expect(/<a href="\/users"/)
.expect(/<a href="\/file%20%231.txt"/)
.expect(/<a href="\/todo.txt"/)
.expect(200, done)
});
it('should respond with testing template sentence', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /This is the test template/, done)
});
it('should have default styles', function (done) {
request(server)
.get('/')
.set('Accept', 'text/html')
.expect(200, /ul#files/, done)
});
it('should list directory twice', function (done) {
request(server)
.get('/users/')
.set('Accept', 'text/html')
.expect(function (res) {
var occurances = res.text.match(/directory \/users\//g)
if (occurances && occurances.length === 2) return
throw new Error('directory not listed twice')
})
.expect(200, done)
}); });
}); });
@@ -530,7 +765,7 @@ describe('serveIndex(root)', function () {
.expect(200) .expect(200)
.expect('Content-Type', 'text/html; charset=utf-8') .expect('Content-Type', 'text/html; charset=utf-8')
.expect(/color: #00ff00;/) .expect(/color: #00ff00;/)
.end(done); .end(done)
}); });
}); });
@@ -576,7 +811,7 @@ describe('serveIndex(root)', function () {
request(server) request(server)
.get('/../') .get('/../')
.set('Accept', 'text/html') .set('Accept', 'text/html')
.expect(403, done); .expect(403, done)
}); });
}); });
}); });
@@ -611,3 +846,9 @@ function bodyDoesNotContain(text) {
assert.equal(res.text.indexOf(text), -1) assert.equal(res.text.indexOf(text), -1)
} }
} }
function shouldNotHaveBody () {
return function (res) {
assert.ok(res.text === '' || res.text === undefined)
}
}