Skip to content
Draft
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
47 changes: 46 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,37 @@ const js = require('@eslint/js');
const tseslint = require('typescript-eslint');
const globals = require('globals');

/**
* JSX the webview views must not contain.
*
* @param {{ allowRawHtml?: boolean }} [options] `allowRawHtml` admits `dangerouslySetInnerHTML`, for `Page.tsx` only.
* @returns {{ selector: string, message: string }[]} `no-restricted-syntax` entries.
*/
function viewRestrictions({ allowRawHtml = false } = {}) {
return [
{
selector: "JSXAttribute[name.name='style']",
message: 'Inline styles are blocked by the webview CSP; add a class in src/webview/styles.',
},
{
selector: 'JSXAttribute[name.name=/^on/]',
message: 'Inline handlers are blocked by the webview CSP; use a data-action attribute.',
},
{
selector: "JSXOpeningElement[name.name='script']:not(:has(JSXAttribute[name.name=/^(src|type)$/]))",
message: 'Inline scripts are blocked by the webview CSP; put code in src/webview/client.',
},
{
selector: "JSXOpeningElement[name.name='style']",
message: 'Inline styles are blocked by the webview CSP; add a stylesheet in src/webview/styles.',
},
...(allowRawHtml ? [] : [{
selector: "JSXAttribute[name.name='dangerouslySetInnerHTML']",
message: 'Raw HTML bypasses escaping; use JsonScript or InlineMarkdown from Page.tsx.',
}]),
];
}

/** @type {import('eslint').Linter.Config[]} */
module.exports = [
{
Expand All @@ -20,7 +51,7 @@ module.exports = [
},
},
...tseslint.config({
files: ['**/*.ts'],
files: ['**/*.ts', '**/*.tsx'],
extends: [...tseslint.configs.recommended],
rules: {
'@typescript-eslint/no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
Expand Down Expand Up @@ -51,6 +82,20 @@ module.exports = [
...js.configs.recommended.rules,
},
},
{
// Webview views render under a nonce CSP: inline styles and handlers are blocked, and raw HTML bypasses escaping.
files: ['src/webview/views/**/*.tsx'],
rules: {
'no-restricted-syntax': ['error', ...viewRestrictions()],
},
},
{
// The two helpers that emit pre-sanitised content are the only place raw HTML is allowed.
files: ['src/webview/views/Page.tsx'],
rules: {
'no-restricted-syntax': ['error', ...viewRestrictions({ allowRawHtml: true })],
},
},
{
// Webview client scripts run in the Electron renderer, not the extension host
files: ['src/webview/client/**/*.ts'],
Expand Down
35 changes: 33 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "gitlab-component-helper",
"displayName": "GitLab Component Helper",
"description": "Provides intellisense for GitLab CI components",
"version": "0.18.1",
"version": "0.19.0",
"icon": "images/icon.png",
"engines": {
"node": ">=22.0.0",
Expand Down Expand Up @@ -349,6 +349,8 @@
"minimatch": "^10.2.6",
"mocha": "^12.0.2",
"npm-run-all": "^4.1.5",
"preact": "^10.29.8",
"preact-render-to-string": "^6.7.0",
"release-it": "^21.1.0",
"semver": "^7.8.5",
"stylelint": "^17.15.0",
Expand Down
Loading
Loading