Skip to content

Commit 3987991

Browse files
feat: implement whitelist for self-hosted instances (#3939)
* whitelist for username based endpoints * added gist whitelist * review * fix --------- Co-authored-by: Alexandr <[email protected]>
1 parent 70add4c commit 3987991

File tree

11 files changed

+168
-32
lines changed

11 files changed

+168
-32
lines changed

api/gist.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import {
44
renderError,
55
parseBoolean,
66
} from "../src/common/utils.js";
7+
import { gistWhitelist } from "../src/common/whitelist.js";
78
import { isLocaleAvailable } from "../src/translations.js";
89
import { renderGistCard } from "../src/cards/gist.js";
910
import { fetchGist } from "../src/fetchers/gist.js";
@@ -26,6 +27,23 @@ export default async (req, res) => {
2627

2728
res.setHeader("Content-Type", "image/svg+xml");
2829

30+
if (gistWhitelist && !gistWhitelist.includes(id)) {
31+
return res.send(
32+
renderError(
33+
"This gist ID is not whitelisted",
34+
"Please deploy your own instance",
35+
{
36+
title_color,
37+
text_color,
38+
bg_color,
39+
border_color,
40+
theme,
41+
show_repo_link: false,
42+
},
43+
),
44+
);
45+
}
46+
2947
if (locale && !isLocaleAvailable(locale)) {
3048
return res.send(
3149
renderError("Something went wrong", "Language not found", {

api/index.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { renderStatsCard } from "../src/cards/stats.js";
22
import { blacklist } from "../src/common/blacklist.js";
3+
import { whitelist } from "../src/common/whitelist.js";
34
import {
45
clampValue,
56
CONSTANTS,
@@ -41,15 +42,37 @@ export default async (req, res) => {
4142
} = req.query;
4243
res.setHeader("Content-Type", "image/svg+xml");
4344

44-
if (blacklist.includes(username)) {
45+
if (whitelist && !whitelist.includes(username)) {
4546
return res.send(
46-
renderError("Something went wrong", "This username is blacklisted", {
47-
title_color,
48-
text_color,
49-
bg_color,
50-
border_color,
51-
theme,
52-
}),
47+
renderError(
48+
"This username is not whitelisted",
49+
"Please deploy your own instance",
50+
{
51+
title_color,
52+
text_color,
53+
bg_color,
54+
border_color,
55+
theme,
56+
show_repo_link: false,
57+
},
58+
),
59+
);
60+
}
61+
62+
if (whitelist === undefined && blacklist.includes(username)) {
63+
return res.send(
64+
renderError(
65+
"This username is blacklisted",
66+
"Please deploy your own instance",
67+
{
68+
title_color,
69+
text_color,
70+
bg_color,
71+
border_color,
72+
theme,
73+
show_repo_link: false,
74+
},
75+
),
5376
);
5477
}
5578

api/pin.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { renderRepoCard } from "../src/cards/repo.js";
22
import { blacklist } from "../src/common/blacklist.js";
3+
import { whitelist } from "../src/common/whitelist.js";
34
import {
45
clampValue,
56
CONSTANTS,
@@ -29,15 +30,37 @@ export default async (req, res) => {
2930

3031
res.setHeader("Content-Type", "image/svg+xml");
3132

32-
if (blacklist.includes(username)) {
33+
if (whitelist && !whitelist.includes(username)) {
3334
return res.send(
34-
renderError("Something went wrong", "This username is blacklisted", {
35-
title_color,
36-
text_color,
37-
bg_color,
38-
border_color,
39-
theme,
40-
}),
35+
renderError(
36+
"This username is not whitelisted",
37+
"Please deploy your own instance",
38+
{
39+
title_color,
40+
text_color,
41+
bg_color,
42+
border_color,
43+
theme,
44+
show_repo_link: false,
45+
},
46+
),
47+
);
48+
}
49+
50+
if (whitelist === undefined && blacklist.includes(username)) {
51+
return res.send(
52+
renderError(
53+
"This username is blacklisted",
54+
"Please deploy your own instance",
55+
{
56+
title_color,
57+
text_color,
58+
bg_color,
59+
border_color,
60+
theme,
61+
show_repo_link: false,
62+
},
63+
),
4164
);
4265
}
4366

api/top-langs.js

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { renderTopLanguages } from "../src/cards/top-languages.js";
22
import { blacklist } from "../src/common/blacklist.js";
3+
import { whitelist } from "../src/common/whitelist.js";
34
import {
45
clampValue,
56
CONSTANTS,
@@ -36,15 +37,37 @@ export default async (req, res) => {
3637
} = req.query;
3738
res.setHeader("Content-Type", "image/svg+xml");
3839

39-
if (blacklist.includes(username)) {
40+
if (whitelist && !whitelist.includes(username)) {
4041
return res.send(
41-
renderError("Something went wrong", "This username is blacklisted", {
42-
title_color,
43-
text_color,
44-
bg_color,
45-
border_color,
46-
theme,
47-
}),
42+
renderError(
43+
"This username is not whitelisted",
44+
"Please deploy your own instance",
45+
{
46+
title_color,
47+
text_color,
48+
bg_color,
49+
border_color,
50+
theme,
51+
show_repo_link: false,
52+
},
53+
),
54+
);
55+
}
56+
57+
if (whitelist === undefined && blacklist.includes(username)) {
58+
return res.send(
59+
renderError(
60+
"This username is blacklisted",
61+
"Please deploy your own instance",
62+
{
63+
title_color,
64+
text_color,
65+
bg_color,
66+
border_color,
67+
theme,
68+
show_repo_link: false,
69+
},
70+
),
4871
);
4972
}
5073

api/wakatime.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
parseBoolean,
77
renderError,
88
} from "../src/common/utils.js";
9+
import { whitelist } from "../src/common/whitelist.js";
910
import { fetchWakatimeStats } from "../src/fetchers/wakatime.js";
1011
import { isLocaleAvailable } from "../src/translations.js";
1112

@@ -36,6 +37,23 @@ export default async (req, res) => {
3637

3738
res.setHeader("Content-Type", "image/svg+xml");
3839

40+
if (whitelist && !whitelist.includes(username)) {
41+
return res.send(
42+
renderError(
43+
"This username is not whitelisted",
44+
"Please deploy your own instance",
45+
{
46+
title_color,
47+
text_color,
48+
bg_color,
49+
border_color,
50+
theme,
51+
show_repo_link: false,
52+
},
53+
),
54+
);
55+
}
56+
3957
if (locale && !isLocaleAvailable(locale)) {
4058
return res.send(
4159
renderError("Something went wrong", "Language not found", {

readme.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@
8686
- [On Vercel](#on-vercel)
8787
- [:film\_projector: Check Out Step By Step Video Tutorial By @codeSTACKr](#film_projector-check-out-step-by-step-video-tutorial-by-codestackr)
8888
- [On other platforms](#on-other-platforms)
89-
- [Disable rate limit protections](#disable-rate-limit-protections)
89+
- [Available environment variables](#available-environment-variables)
9090
- [Keep your fork up to date](#keep-your-fork-up-to-date)
9191
- [:sparkling\_heart: Support the project](#sparkling_heart-support-the-project)
9292
</details>
@@ -797,11 +797,13 @@ Since the GitHub API only allows 5k requests per hour, my `https://github-readme
797797
5. You're done 🎉
798798
</details>
799799

800-
## Disable rate limit protections
800+
## Available environment variables
801801

802-
GitHub Readme Stats contains several Vercel environment variables that can be used to remove the rate limit protections:
802+
GitHub Readme Stats provides several environment variables that can be used to customize the behavior of your self-hosted instance. These include:
803803

804-
* `CACHE_SECONDS`: This environment variable takes precedence over our cache minimum and maximum values and can circumvent these values for self-hosted Vercel instances.
804+
* `CACHE_SECONDS`: This takes precedence over our cache minimum and maximum values and can circumvent these values for self-hosted instances.
805+
* `WHITELIST`: A comma-separated list of GitHub usernames that are allowed to access your instance. If this variable is not set, all usernames are allowed.
806+
* `GIST_WHITELIST`: A comma-separated list of GitHub gist IDs that are allowed to be accessed on your instance. If this variable is not set, all gist IDs are allowed.
805807

806808
See [the Vercel documentation](https://vercel.com/docs/concepts/projects/environment-variables) on adding these environment variables to your Vercel instance.
807809

src/common/utils.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,12 @@ const UPSTREAM_API_ERRORS = [
355355
* @param {string} message Main error message.
356356
* @param {string} secondaryMessage The secondary error message.
357357
* @param {object} options Function options.
358+
* @param {string=} options.title_color Card title color.
359+
* @param {string=} options.text_color Card text color.
360+
* @param {string=} options.bg_color Card background color.
361+
* @param {string=} options.border_color Card border color.
362+
* @param {string=} options.theme Card theme.
363+
* @param {boolean=} options.show_repo_link Whether to show repo link or not.
358364
* @returns {string} The SVG markup.
359365
*/
360366
const renderError = (message, secondaryMessage = "", options = {}) => {
@@ -364,6 +370,7 @@ const renderError = (message, secondaryMessage = "", options = {}) => {
364370
bg_color,
365371
border_color,
366372
theme = "default",
373+
show_repo_link = true,
367374
} = options;
368375

369376
// returns theme based colors with proper overrides and defaults
@@ -388,7 +395,7 @@ const renderError = (message, secondaryMessage = "", options = {}) => {
388395
ERROR_CARD_LENGTH - 1
389396
}" height="99%" rx="4.5" fill="${bgColor}" stroke="${borderColor}"/>
390397
<text x="25" y="45" class="text">Something went wrong!${
391-
UPSTREAM_API_ERRORS.includes(secondaryMessage)
398+
UPSTREAM_API_ERRORS.includes(secondaryMessage) || !show_repo_link
392399
? ""
393400
: " file an issue at https://tiny.one/readme-stats"
394401
}</text>

src/common/whitelist.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
const whitelist = process.env.WHITELIST
2+
? process.env.WHITELIST.split(",")
3+
: undefined;
4+
5+
const gistWhitelist = process.env.GIST_WHITELIST
6+
? process.env.GIST_WHITELIST.split(",")
7+
: undefined;
8+
9+
export { whitelist, gistWhitelist };
10+
export default whitelist;

tests/api.test.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,11 @@ describe("Test /api/", () => {
307307

308308
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
309309
expect(res.send).toBeCalledWith(
310-
renderError("Something went wrong", "This username is blacklisted"),
310+
renderError(
311+
"This username is blacklisted",
312+
"Please deploy your own instance",
313+
{ show_repo_link: false },
314+
),
311315
);
312316
});
313317

tests/pin.test.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ describe("Test /api/pin", () => {
156156

157157
expect(res.setHeader).toBeCalledWith("Content-Type", "image/svg+xml");
158158
expect(res.send).toBeCalledWith(
159-
renderError("Something went wrong", "This username is blacklisted"),
159+
renderError(
160+
"This username is blacklisted",
161+
"Please deploy your own instance",
162+
{ show_repo_link: false },
163+
),
160164
);
161165
});
162166

0 commit comments

Comments
 (0)