Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,11 @@ to "deny").
- `'ignore'` Pretend like the dotfile does not exist and 404.

The default value is _similar_ to `'ignore'`, with the exception that
this default will not ignore the files within a directory that begins
with a dot, for backward-compatibility.
files within the top-level `.well-known` directory ([RFC 8615
well-known URIs][rfc-8615], such as ACME challenges) are not ignored.
Set this option explicitly to disable that exception.

[rfc-8615]: https://www.rfc-editor.org/rfc/rfc8615

##### end

Expand Down
28 changes: 26 additions & 2 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,10 @@ function SendStream (req, path, options) {
throw new TypeError('dotfiles option must be "allow", "deny", or "ignore"')
}

// serve well-known URIs (RFC 8615, "/.well-known/*") when no explicit
// dotfiles option was provided
this._wellKnown = opts.dotfiles === undefined

this._extensions = opts.extensions !== undefined
? normalizeList(opts.extensions, 'extensions option')
: []
Expand Down Expand Up @@ -456,8 +460,16 @@ SendStream.prototype.pipe = function pipe (res) {

// dotfile handling
if (containsDotFile(parts)) {
debug('%s dotfile "%s"', this._dotfiles, path)
switch (this._dotfiles) {
var access = this._dotfiles

// serve well-known URIs (RFC 8615, "/.well-known/*") when no explicit
// dotfiles option was provided
if (this._wellKnown && isWellKnown(parts)) {
access = 'allow'
}

debug('%s dotfile "%s"', access, path)
switch (access) {
case 'allow':
break
case 'deny':
Expand Down Expand Up @@ -815,6 +827,18 @@ function containsDotFile (parts) {
return false
}

/**
* Determine if path parts are within the well-known directory
* (RFC 8615, "/.well-known/") and contain no other dotfile parts.
*
* @api private
*/

function isWellKnown (parts) {
return parts.length > 1 && parts[0] === '.well-known' &&
!containsDotFile(parts.slice(1))
}

/**
* Create a Content-Range header.
*
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/.well-known/.hidden
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
secret
1 change: 1 addition & 0 deletions test/fixtures/.well-known/security.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Contact: mailto:security@example.com
24 changes: 24 additions & 0 deletions test/send.js
Original file line number Diff line number Diff line change
Expand Up @@ -941,6 +941,24 @@ describe('send(file, options)', function () {
.expect(404, done)
})

it('should serve file within well-known directory', function (done) {
request(createServer({ root: fixtures }))
.get('/.well-known/security.txt')
.expect(200, 'Contact: mailto:security@example.com', done)
})

it('should ignore dotfile within well-known directory', function (done) {
request(createServer({ root: fixtures }))
.get('/.well-known/.hidden')
.expect(404, done)
})

it('should ignore well-known directory when dotfiles is explicitly "ignore"', function (done) {
request(createServer({ dotfiles: 'ignore', root: fixtures }))
.get('/.well-known/security.txt')
.expect(404, done)
})

it('should reject bad value', function (done) {
request(createServer({ dotfiles: 'bogus' }))
.get('/name.txt')
Expand Down Expand Up @@ -974,6 +992,12 @@ describe('send(file, options)', function () {
.expect(403, done)
})

it('should 403 for file within well-known directory', function (done) {
request(createServer({ dotfiles: 'deny', root: fixtures }))
.get('/.well-known/security.txt')
.expect(403, done)
})

it('should 403 for dotfile directory', function (done) {
request(createServer({ dotfiles: 'deny', root: fixtures }))
.get('/.mine')
Expand Down