@devvit/public-api 0.11.5-next-2024-12-16-92b65c499.0 → 0.11.5-next-2024-12-16-0efd28179.0
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/devvit/internals/csrf.d.ts.map +1 -1
- package/devvit/internals/csrf.js +54 -24
- package/meta.json +3 -3
- package/meta.min.json +5 -5
- package/package.json +6 -6
- package/public-api.iife.js +33 -25
- package/public-api.min.js +5 -5
- package/public-api.min.js.map +3 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../../src/devvit/internals/csrf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGjF,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"csrf.d.ts","sourceRoot":"","sources":["../../../src/devvit/internals/csrf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGjF,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAuB7E,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,WAAW,GAAG,iBAAiB,EACxC,GAAG,EAAE,oBAAoB,iBAmF1B;AAWD,wBAAsB,iBAAiB,CACrC,OAAO,EAAE,WAAW,GAAG,iBAAiB,EACxC,GAAG,EAAE,oBAAoB,iBAc1B"}
|
package/devvit/internals/csrf.js
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import { Header } from '@devvit/shared-types/Header.js';
|
|
2
2
|
import { getMenuItemById } from './menu-items.js';
|
|
3
|
+
function getUserHeader(context) {
|
|
4
|
+
const userHeader = context.debug.metadata[Header.User].values[0];
|
|
5
|
+
if (!userHeader) {
|
|
6
|
+
throw new Error('User missing from context');
|
|
7
|
+
}
|
|
8
|
+
return userHeader;
|
|
9
|
+
}
|
|
10
|
+
function getSubredditHeader(context) {
|
|
11
|
+
const subredditHeader = context.debug.metadata[Header.Subreddit].values[0];
|
|
12
|
+
if (!subredditHeader) {
|
|
13
|
+
throw new Error('Subreddit missing from context');
|
|
14
|
+
}
|
|
15
|
+
return subredditHeader;
|
|
16
|
+
}
|
|
3
17
|
export async function addCSRFTokenToContext(context, req) {
|
|
18
|
+
/**
|
|
19
|
+
* These headers represent the canonical user and subreddit for the current context. The user is validated by gateway against
|
|
20
|
+
* the edge context, so we trust it.
|
|
21
|
+
*/
|
|
22
|
+
const userHeader = getUserHeader(context);
|
|
23
|
+
const subredditHeader = getSubredditHeader(context);
|
|
24
|
+
if (!userHeader || !subredditHeader) {
|
|
25
|
+
throw new Error('User or subreddit missing from context');
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* These are the target ids for the action. They are provided by the user, but we need to validate them.
|
|
29
|
+
*/
|
|
4
30
|
const commentId = req.comment?.id && `t1_${req.comment.id}`;
|
|
5
31
|
const postId = req.post?.id && `t3_${req.post.id}`;
|
|
6
32
|
const subredditId = req.subreddit?.id && `t5_${req.subreddit.id}`;
|
|
@@ -8,32 +34,39 @@ export async function addCSRFTokenToContext(context, req) {
|
|
|
8
34
|
if (!targetId) {
|
|
9
35
|
throw new Error('targetId is missing from ContextActionRequest');
|
|
10
36
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
throw new Error('User or subreddit missing from context');
|
|
15
|
-
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate user-provided commentId
|
|
39
|
+
*/
|
|
16
40
|
if (commentId) {
|
|
17
41
|
const comment = await context.reddit.getCommentById(commentId);
|
|
18
42
|
if (!comment) {
|
|
19
43
|
throw new Error(`Comment ${commentId} not found`);
|
|
20
44
|
}
|
|
21
|
-
if (comment.subredditId !==
|
|
22
|
-
throw new Error(
|
|
45
|
+
if (comment.subredditId !== subredditHeader) {
|
|
46
|
+
throw new Error(`Comment does not belong to the subreddit`);
|
|
23
47
|
}
|
|
24
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Validate user-provided postId
|
|
51
|
+
*/
|
|
25
52
|
if (postId) {
|
|
26
53
|
const post = await context.reddit.getPostById(postId);
|
|
27
54
|
if (!post) {
|
|
28
55
|
throw new Error(`Post ${postId} not found`);
|
|
29
56
|
}
|
|
30
|
-
if (post.subredditId !==
|
|
31
|
-
throw new Error(
|
|
57
|
+
if (post.subredditId !== subredditHeader) {
|
|
58
|
+
throw new Error(`Post does not belong to the subreddit`);
|
|
32
59
|
}
|
|
33
60
|
}
|
|
61
|
+
/**
|
|
62
|
+
* Validate user-provided subredditId
|
|
63
|
+
*/
|
|
34
64
|
if (subredditId && subredditId !== subredditHeader) {
|
|
35
65
|
throw new Error(`Subreddit ${subredditId} ${subredditHeader} not found`);
|
|
36
66
|
}
|
|
67
|
+
/**
|
|
68
|
+
* Validate moderator status if needed
|
|
69
|
+
*/
|
|
37
70
|
const thisItem = getMenuItemById(req.actionId);
|
|
38
71
|
if (!thisItem) {
|
|
39
72
|
throw new Error('Action not found');
|
|
@@ -45,34 +78,31 @@ export async function addCSRFTokenToContext(context, req) {
|
|
|
45
78
|
const val = {
|
|
46
79
|
needsModCheck,
|
|
47
80
|
};
|
|
48
|
-
|
|
49
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Store a CSRF token in redis for this action. There's no way to pass this along, so we need to store it in redis.
|
|
83
|
+
*/
|
|
84
|
+
await context.redis.set(`${userHeader}${subredditHeader}${targetId}${req.actionId}`, JSON.stringify(val));
|
|
85
|
+
await context.redis.expire(`${userHeader}${subredditHeader}${targetId}${req.actionId}`, 600);
|
|
50
86
|
console.debug('CSRF token added: ' + JSON.stringify(val));
|
|
51
87
|
}
|
|
52
88
|
async function isModerator(context) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
throw new Error('User missing from context');
|
|
56
|
-
}
|
|
57
|
-
const subredditHeader = context.debug.metadata[Header.Subreddit].values[0];
|
|
58
|
-
if (!subredditHeader) {
|
|
59
|
-
throw new Error('Subreddit missing from context');
|
|
60
|
-
}
|
|
89
|
+
const userHeader = getUserHeader(context);
|
|
90
|
+
const subredditHeader = getSubredditHeader(context);
|
|
61
91
|
const subreddit = await context.reddit.getSubredditInfoById(subredditHeader);
|
|
62
92
|
const mods = await context.reddit.getModerators({ subredditName: subreddit.name }).all();
|
|
63
|
-
return !!mods.find((mod) => mod.id ===
|
|
93
|
+
return !!mods.find((mod) => mod.id === userHeader);
|
|
64
94
|
}
|
|
65
95
|
export async function validateCSRFToken(context, req) {
|
|
66
|
-
const
|
|
67
|
-
const subredditHeader = context
|
|
96
|
+
const userHeader = getUserHeader(context);
|
|
97
|
+
const subredditHeader = getSubredditHeader(context);
|
|
68
98
|
const { actionId, thingId } = req.state?.__contextAction ?? {};
|
|
69
|
-
const csrfData = await context.redis.get(`${
|
|
99
|
+
const csrfData = await context.redis.get(`${userHeader}${subredditHeader}${thingId}${actionId}`);
|
|
70
100
|
if (!csrfData) {
|
|
71
101
|
throw new Error('CSRF token not found');
|
|
72
102
|
}
|
|
73
103
|
const csrf = JSON.parse(csrfData);
|
|
74
104
|
if (csrf.needsModCheck && !(await isModerator(context))) {
|
|
75
|
-
throw new Error('User is not a moderator: ' +
|
|
105
|
+
throw new Error('User is not a moderator: ' + userHeader + '; ' + subredditHeader);
|
|
76
106
|
}
|
|
77
107
|
console.debug('CSRF token validated: ' + csrfData);
|
|
78
108
|
}
|
package/meta.json
CHANGED
|
@@ -10411,7 +10411,7 @@
|
|
|
10411
10411
|
"format": "esm"
|
|
10412
10412
|
},
|
|
10413
10413
|
"src/devvit/internals/csrf.ts": {
|
|
10414
|
-
"bytes":
|
|
10414
|
+
"bytes": 4559,
|
|
10415
10415
|
"imports": [
|
|
10416
10416
|
{
|
|
10417
10417
|
"path": "../shared-types/dist/Header.js",
|
|
@@ -12797,7 +12797,7 @@
|
|
|
12797
12797
|
"bytesInOutput": 663
|
|
12798
12798
|
},
|
|
12799
12799
|
"src/devvit/internals/csrf.ts": {
|
|
12800
|
-
"bytesInOutput":
|
|
12800
|
+
"bytesInOutput": 3505
|
|
12801
12801
|
},
|
|
12802
12802
|
"src/devvit/internals/menu-items.ts": {
|
|
12803
12803
|
"bytesInOutput": 2374
|
|
@@ -12944,7 +12944,7 @@
|
|
|
12944
12944
|
"bytesInOutput": 370
|
|
12945
12945
|
}
|
|
12946
12946
|
},
|
|
12947
|
-
"bytes":
|
|
12947
|
+
"bytes": 12714715
|
|
12948
12948
|
}
|
|
12949
12949
|
}
|
|
12950
12950
|
}
|
package/meta.min.json
CHANGED
|
@@ -3271,7 +3271,7 @@
|
|
|
3271
3271
|
"format": "esm"
|
|
3272
3272
|
},
|
|
3273
3273
|
"src/devvit/internals/csrf.ts": {
|
|
3274
|
-
"bytes":
|
|
3274
|
+
"bytes": 4559,
|
|
3275
3275
|
"imports": [
|
|
3276
3276
|
{
|
|
3277
3277
|
"path": "../shared-types/dist/Header.js",
|
|
@@ -4815,7 +4815,7 @@
|
|
|
4815
4815
|
"imports": [],
|
|
4816
4816
|
"exports": [],
|
|
4817
4817
|
"inputs": {},
|
|
4818
|
-
"bytes":
|
|
4818
|
+
"bytes": 1241211
|
|
4819
4819
|
},
|
|
4820
4820
|
"dist/public-api.min.js": {
|
|
4821
4821
|
"imports": [
|
|
@@ -5522,7 +5522,7 @@
|
|
|
5522
5522
|
"bytesInOutput": 1322
|
|
5523
5523
|
},
|
|
5524
5524
|
"src/devvit/internals/csrf.ts": {
|
|
5525
|
-
"bytesInOutput":
|
|
5525
|
+
"bytesInOutput": 1802
|
|
5526
5526
|
},
|
|
5527
5527
|
"src/devvit/internals/plugins.ts": {
|
|
5528
5528
|
"bytesInOutput": 47
|
|
@@ -5621,7 +5621,7 @@
|
|
|
5621
5621
|
"bytesInOutput": 4081
|
|
5622
5622
|
},
|
|
5623
5623
|
"src/apis/reddit/models/Post.ts": {
|
|
5624
|
-
"bytesInOutput":
|
|
5624
|
+
"bytesInOutput": 14651
|
|
5625
5625
|
},
|
|
5626
5626
|
"src/apis/reddit/helpers/textFallbackToRichtext.ts": {
|
|
5627
5627
|
"bytesInOutput": 114
|
|
@@ -5663,7 +5663,7 @@
|
|
|
5663
5663
|
"bytesInOutput": 178
|
|
5664
5664
|
}
|
|
5665
5665
|
},
|
|
5666
|
-
"bytes":
|
|
5666
|
+
"bytes": 264805
|
|
5667
5667
|
}
|
|
5668
5668
|
}
|
|
5669
5669
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@devvit/public-api",
|
|
3
|
-
"version": "0.11.5-next-2024-12-16-
|
|
3
|
+
"version": "0.11.5-next-2024-12-16-0efd28179.0",
|
|
4
4
|
"license": "BSD-3-Clause",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -30,8 +30,8 @@
|
|
|
30
30
|
},
|
|
31
31
|
"types": "./index.d.ts",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@devvit/protos": "0.11.5-next-2024-12-16-
|
|
34
|
-
"@devvit/shared-types": "0.11.5-next-2024-12-16-
|
|
33
|
+
"@devvit/protos": "0.11.5-next-2024-12-16-0efd28179.0",
|
|
34
|
+
"@devvit/shared-types": "0.11.5-next-2024-12-16-0efd28179.0",
|
|
35
35
|
"base64-js": "1.5.1",
|
|
36
36
|
"clone-deep": "4.0.1",
|
|
37
37
|
"core-js": "3.27.2",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@ampproject/filesize": "4.3.0",
|
|
42
|
-
"@devvit/repo-tools": "0.11.5-next-2024-12-16-
|
|
43
|
-
"@devvit/tsconfig": "0.11.5-next-2024-12-16-
|
|
42
|
+
"@devvit/repo-tools": "0.11.5-next-2024-12-16-0efd28179.0",
|
|
43
|
+
"@devvit/tsconfig": "0.11.5-next-2024-12-16-0efd28179.0",
|
|
44
44
|
"@microsoft/api-extractor": "7.41.0",
|
|
45
45
|
"@reddit/faceplate-ui": "18.0.1",
|
|
46
46
|
"@types/clone-deep": "4.0.1",
|
|
@@ -63,5 +63,5 @@
|
|
|
63
63
|
}
|
|
64
64
|
},
|
|
65
65
|
"source": "./src/index.ts",
|
|
66
|
-
"gitHead": "
|
|
66
|
+
"gitHead": "180d476556d692e3428a7c699669a8b8fb747b59"
|
|
67
67
|
}
|