Skip to content

Commit 6a30b3b

Browse files
fix: prevent XSS in profile fields and repair theme/CSS/CI bugs (#10)
1 parent 6e72ed0 commit 6a30b3b

5 files changed

Lines changed: 40 additions & 36 deletions

File tree

.github/workflows/main.yml

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,34 +3,24 @@ on:
33
push:
44
branches:
55
- master
6+
pull_request:
67
jobs:
78
build:
89
runs-on: ubuntu-latest
910

1011
steps:
11-
- name: Begin CI...
12-
uses: actions/checkout@v2
12+
- name: Checkout
13+
uses: actions/checkout@v4
1314

14-
- name: Use Node 12
15-
uses: actions/setup-node@v1
15+
- name: Use Node 20
16+
uses: actions/setup-node@v4
1617
with:
17-
node-version: 12.x
18-
registry-url: 'https://registry.npmjs.org'
19-
20-
- name: Use cached node_modules
21-
uses: actions/cache@v2
22-
with:
23-
path: node_modules
24-
key: nodeModules-${{ hashFiles('**/yarn.lock') }}
25-
restore-keys: |
26-
nodeModules-
18+
node-version: 20.x
2719

20+
# --legacy-peer-deps tolerates rollup-plugin-uglify's stale rollup<2 peer
21+
# range. Drop this once the build plugin is replaced (e.g. @rollup/plugin-terser).
2822
- name: Install dependencies
29-
run: yarn install --frozen-lockfile
30-
env:
31-
CI: true
23+
run: npm install --legacy-peer-deps
3224

3325
- name: Lint
34-
run: yarn lint
35-
env:
36-
CI: true
26+
run: npm run lint
File renamed without changes.

src/escape.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Escape user-controlled values (e.g. GitHub name/bio) before injecting them
2+
// into the card's innerHTML, to prevent HTML/attribute injection.
3+
function escapeHtml(value) {
4+
return String(value)
5+
.replace(/&/g, '&amp;')
6+
.replace(/</g, '&lt;')
7+
.replace(/>/g, '&gt;')
8+
.replace(/"/g, '&quot;')
9+
.replace(/'/g, '&#39;');
10+
}
11+
12+
export default escapeHtml;

src/index.js

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* eslint no-undef: 0 */
22
import widgetStyle from './style.js';
33
import nFormatter from './format.js';
4+
import escapeHtml from './escape.js';
45

56
const template = document.createElement('template');
67

@@ -19,13 +20,13 @@ class myCard extends HTMLElement {
1920
this._shadowRoot.append(template.content.cloneNode(true));
2021
}
2122

22-
get observedAttribute() {
23+
static get observedAttributes() {
2324
return ['data-theme'];
2425
}
2526

2627
attributeChangedCallback(attr, oldValue, newValue) {
27-
if (attr === 'data-theme' && oldValue !== newValue && newValue !== '') {
28-
this[attr] = newValue;
28+
if (attr === 'data-theme' && oldValue !== newValue) {
29+
this.setTheme(newValue);
2930
}
3031
}
3132

@@ -34,11 +35,6 @@ class myCard extends HTMLElement {
3435
const cardTemplate = this.createCard(response);
3536
this._shadowRoot.querySelector('.card').innerHTML = cardTemplate;
3637
});
37-
38-
if (this.getAttribute('data-theme')) {
39-
const theme = this.getAttribute('data-theme');
40-
this.setTheme(theme);
41-
}
4238
}
4339

4440
setTheme(theme) {
@@ -62,31 +58,37 @@ class myCard extends HTMLElement {
6258
}
6359

6460
createCard(user) {
61+
const login = escapeHtml(user.login);
62+
const htmlUrl = escapeHtml(user.html_url);
63+
const name = escapeHtml(user.name);
64+
const bio = escapeHtml(user.bio ?? '');
6565
return `
6666
<div class="cover"></div>
6767
<div class="card-wrapper">
68-
<a href="https://github.com/${user.login}" target="_blank" rel="noopener"><img id="github-logo" src="https://i.ibb.co/frv5pB3/github-logo.png" alt="github-logo" border="0"></a>
68+
<a href="https://github.com/${login}" target="_blank" rel="noopener"><img id="github-logo" src="https://i.ibb.co/frv5pB3/github-logo.png" alt="github-logo" border="0"></a>
6969
<div class="card-header">
70-
<div class="card-img-wrapper"><img src="https://avatars.githubusercontent.com/${user.login}"/></div>
71-
<h1><a class="card-title" href="${user.html_url}" target="_blank" rel="noopener">${user.name}</a></h1>
72-
<div class="card-responsename"><a href="${user.html_url}" target="_blank" rel="noopener">@${user.login}</a></div>
73-
<p class="card-desc">${user.bio ?? ''}</p>
70+
<div class="card-img-wrapper"><img src="https://avatars.githubusercontent.com/${login}" alt="${name}"/></div>
71+
<h1><a class="card-title" href="${htmlUrl}" target="_blank" rel="noopener">${name}</a></h1>
72+
<div class="card-responsename"><a href="${htmlUrl}" target="_blank" rel="noopener">@${login}</a></div>
73+
<p class="card-desc">${bio}</p>
7474
<div class="card-footer">
7575
<div class="footer-box">
7676
<div class="box-wrapper">
7777
<div class="count">${nFormatter(user.followers)}</div>
7878
<div class="box-text">Followers</div>
79-
</div>
79+
</div>
8080
<div class="box-wrapper">
8181
<div class="count">${nFormatter(user.following)}</div>
8282
<div class="box-text">Following</div>
83-
</div>
83+
</div>
8484
<div class="box-wrapper">
8585
<div class="count">${nFormatter(user.public_repos)}</div>
8686
<div class="box-text">Repositories</div>
8787
</div>
8888
</div>
8989
</div>
90+
</div>
91+
</div>
9092
`;
9193
}
9294
}

src/style.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ a{
118118
position: relative;
119119
}
120120
.card .card-footer .footer-box .box-wrapper .count {
121-
font-family: 'consolas'
121+
font-family: 'consolas';
122122
color: #434343;
123123
font-size: 20px;
124124
font-weight: 600;

0 commit comments

Comments
 (0)