@evanp/activitypub-bot 0.47.1 → 0.48.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/lib/app.js +4 -0
- package/lib/httpsignatureauthenticator.js +12 -12
- package/lib/remotekeystorage.js +12 -2
- package/lib/routes/favicon.js +16 -0
- package/lib/routes/robotstxt.js +15 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,19 @@ and this project adheres to
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.48.1] - 2026-05-28
|
|
13
|
+
|
|
14
|
+
- 401 Unauthorized for authentication errors
|
|
15
|
+
- 401 Unauthorized when actor has been
|
|
16
|
+
deleted
|
|
17
|
+
|
|
18
|
+
## [0.48.0] - 2026-05-28
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- /favicon.ico with a little robot head emoji
|
|
23
|
+
- very permissive /robots.txt
|
|
24
|
+
|
|
12
25
|
## [0.47.1] - 2026-05-28
|
|
13
26
|
|
|
14
27
|
### Fixed
|
package/lib/app.js
CHANGED
|
@@ -34,6 +34,8 @@ import sharedInboxRouter from './routes/sharedinbox.js'
|
|
|
34
34
|
import profileRouter from './routes/profile.js'
|
|
35
35
|
import proxyRouter from './routes/proxy.js'
|
|
36
36
|
import nodeinfoRouter from './routes/nodeinfo.js'
|
|
37
|
+
import robotsTxtRouter from './routes/robotstxt.js'
|
|
38
|
+
import faviconRouter from './routes/favicon.js'
|
|
37
39
|
import { BotContext } from './botcontext.js'
|
|
38
40
|
import { Transformer } from './microsyntax.js'
|
|
39
41
|
import { HTTPSignatureAuthenticator } from './httpsignatureauthenticator.js'
|
|
@@ -329,6 +331,8 @@ export async function makeApp ({ databaseUrl, origin, bots, logLevel = 'silent',
|
|
|
329
331
|
app.use('/', webfingerRouter)
|
|
330
332
|
app.use('/', proxyRouter)
|
|
331
333
|
app.use('/', nodeinfoRouter)
|
|
334
|
+
app.use('/', robotsTxtRouter)
|
|
335
|
+
app.use('/', faviconRouter)
|
|
332
336
|
|
|
333
337
|
// These should check HTTP Signature if provided
|
|
334
338
|
|
|
@@ -77,21 +77,21 @@ export class HTTPSignatureAuthenticator {
|
|
|
77
77
|
this.#logger.debug({ reqId: req.id, signature }, 'Got signed request')
|
|
78
78
|
const date = req.get('Date')
|
|
79
79
|
if (!date) {
|
|
80
|
-
throw new ProblemDetailsError(
|
|
80
|
+
throw new ProblemDetailsError(401, 'No date provided')
|
|
81
81
|
}
|
|
82
82
|
if (Math.abs(Date.parse(date) - Date.now()) >
|
|
83
83
|
HTTPSignatureAuthenticator.#maxDateDiff) {
|
|
84
|
-
throw new ProblemDetailsError(
|
|
84
|
+
throw new ProblemDetailsError(401, 'Time skew too large')
|
|
85
85
|
}
|
|
86
86
|
if (req.rawBodyText && req.rawBodyText.length > 0) {
|
|
87
87
|
const digest = req.get('Digest')
|
|
88
88
|
if (!digest) {
|
|
89
|
-
throw new ProblemDetailsError(
|
|
89
|
+
throw new ProblemDetailsError(401, 'No digest provided')
|
|
90
90
|
}
|
|
91
91
|
const calculated = await this.#digester.digest(req.rawBodyText)
|
|
92
92
|
if (!this.#digester.equals(digest, calculated)) {
|
|
93
93
|
this.#logger.debug({ reqId: req.id, calculated, digest }, 'Digest mismatch')
|
|
94
|
-
throw new ProblemDetailsError(
|
|
94
|
+
throw new ProblemDetailsError(401, 'Digest mismatch')
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
const { method, headers } = req
|
|
@@ -100,7 +100,7 @@ export class HTTPSignatureAuthenticator {
|
|
|
100
100
|
this.#logger.debug({ reqId: req.id, keyId }, 'Signed with keyId')
|
|
101
101
|
const ok = await this.#remoteKeyStorage.getPublicKey(keyId)
|
|
102
102
|
if (!ok) {
|
|
103
|
-
throw new ProblemDetailsError(
|
|
103
|
+
throw new ProblemDetailsError(401, 'public key not found')
|
|
104
104
|
}
|
|
105
105
|
let owner = ok.owner
|
|
106
106
|
let publicKeyPem = ok.publicKeyPem
|
|
@@ -137,25 +137,25 @@ export class HTTPSignatureAuthenticator {
|
|
|
137
137
|
const date = req.get('Date')
|
|
138
138
|
if (date && Math.abs(Date.parse(date) - Date.now()) >
|
|
139
139
|
HTTPSignatureAuthenticator.#maxDateDiff) {
|
|
140
|
-
throw new ProblemDetailsError(
|
|
140
|
+
throw new ProblemDetailsError(401, 'Time skew too large')
|
|
141
141
|
}
|
|
142
142
|
if (req.rawBodyText && req.rawBodyText.length > 0) {
|
|
143
143
|
const digest = req.get('Content-Digest')
|
|
144
144
|
if (!digest) {
|
|
145
|
-
throw new ProblemDetailsError(
|
|
145
|
+
throw new ProblemDetailsError(401, 'No digest provided')
|
|
146
146
|
}
|
|
147
147
|
const calculated = await this.#digester.contentDigest(req.rawBodyText)
|
|
148
148
|
if (!this.#digester.equals(digest, calculated)) {
|
|
149
149
|
this.#logger.debug({ reqId: req.id, calculated, digest }, 'Digest mismatch')
|
|
150
|
-
throw new ProblemDetailsError(
|
|
150
|
+
throw new ProblemDetailsError(401, 'Digest mismatch')
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
153
|
const created = this.#messageSigner.created(signatureInput)
|
|
154
154
|
if (!created) {
|
|
155
|
-
throw new ProblemDetailsError(
|
|
155
|
+
throw new ProblemDetailsError(401, 'No created timestamp provided')
|
|
156
156
|
}
|
|
157
157
|
if (Math.abs(Date.now() - created * 1000) > HTTPSignatureAuthenticator.#maxDateDiff) {
|
|
158
|
-
throw new ProblemDetailsError(
|
|
158
|
+
throw new ProblemDetailsError(401, 'Time skew too large')
|
|
159
159
|
}
|
|
160
160
|
const { method, headers } = req
|
|
161
161
|
const { origin } = req.app.locals
|
|
@@ -163,12 +163,12 @@ export class HTTPSignatureAuthenticator {
|
|
|
163
163
|
|
|
164
164
|
const keyId = this.#messageSigner.keyId(signatureInput)
|
|
165
165
|
if (!keyId) {
|
|
166
|
-
throw new ProblemDetailsError(
|
|
166
|
+
throw new ProblemDetailsError(401, 'no public key provided')
|
|
167
167
|
}
|
|
168
168
|
this.#logger.debug({ reqId: req.id, keyId }, 'Signed with keyId')
|
|
169
169
|
const ok = await this.#remoteKeyStorage.getPublicKey(keyId)
|
|
170
170
|
if (!ok) {
|
|
171
|
-
throw new ProblemDetailsError(
|
|
171
|
+
throw new ProblemDetailsError(401, 'public key not found')
|
|
172
172
|
}
|
|
173
173
|
let owner = ok.owner
|
|
174
174
|
let publicKeyPem = ok.publicKeyPem
|
package/lib/remotekeystorage.js
CHANGED
|
@@ -31,7 +31,10 @@ export class RemoteKeyStorage {
|
|
|
31
31
|
return null
|
|
32
32
|
}
|
|
33
33
|
if (!await this.#confirmPublicKey(remote.owner, id)) {
|
|
34
|
-
this.#logger.warn(
|
|
34
|
+
this.#logger.warn(
|
|
35
|
+
{ owner: remote.owner, id },
|
|
36
|
+
'Cannot confirm match between owner and public key'
|
|
37
|
+
)
|
|
35
38
|
return null
|
|
36
39
|
}
|
|
37
40
|
await this.#cachePublicKey(id, remote.owner, remote.publicKeyPem)
|
|
@@ -58,7 +61,14 @@ export class RemoteKeyStorage {
|
|
|
58
61
|
|
|
59
62
|
async #getRemotePublicKey (id) {
|
|
60
63
|
this.debug(`#getRemotePublicKey(${id})`)
|
|
61
|
-
|
|
64
|
+
let response
|
|
65
|
+
try {
|
|
66
|
+
response = await this.#client.getKey(id)
|
|
67
|
+
} catch (err) {
|
|
68
|
+
this.#logger.warn({ id, err }, 'error getting key')
|
|
69
|
+
return null
|
|
70
|
+
}
|
|
71
|
+
|
|
62
72
|
if (!response) {
|
|
63
73
|
return null
|
|
64
74
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Router } from 'express'
|
|
2
|
+
|
|
3
|
+
const router = Router()
|
|
4
|
+
|
|
5
|
+
const FAVICON_ICO =
|
|
6
|
+
`<svg
|
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
8
|
+
viewBox="0 0 100 100">
|
|
9
|
+
<text y=".9em" font-size="90">🤖</text>
|
|
10
|
+
</svg>`
|
|
11
|
+
|
|
12
|
+
router.get('/favicon.ico', async (req, res, next) => {
|
|
13
|
+
res.status(200).type('image/svg+xml').end(FAVICON_ICO)
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export default router
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Router } from 'express'
|
|
2
|
+
|
|
3
|
+
const router = Router()
|
|
4
|
+
|
|
5
|
+
const ROBOTS_TXT =
|
|
6
|
+
`# We are all bots here
|
|
7
|
+
User-agent: *
|
|
8
|
+
Disallow:
|
|
9
|
+
`
|
|
10
|
+
|
|
11
|
+
router.get('/robots.txt', async (req, res, next) => {
|
|
12
|
+
res.status(200).type('text/plain').end(ROBOTS_TXT)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
export default router
|