🔒fix-csp-compliance
- プラグイン
- ui5-modernization
- ソース
- GitHub で見る ↗
説明
UI5 linterが報告するが自動修正できない、コンテンツセキュリティポリシー(CSP)準拠の問題を修正します。 次のような場合に使用: - linterが以下を出力している場合 - `csp-unsafe-inline-script`(メッセージ: "Use of unsafe inline script") - HTMLファイル内のインラインJavaScript、`src`属性を持たない`script`タグ、HTMLの`onclick`/`onload`ハンドラーが検出された場合 また、ユーザーが以下のキーワードに言及した場合にも使用: `security headers`、`inline script warning`、`CSP policy`、`unsafe-inline`、`Content-Security-Policy` CSP準拠のため、インラインスクリプトを外部ファイルへ移行するためのガイダンスを提供します。
原文を表示
Fix Content Security Policy (CSP) compliance issues that UI5 linter reports but cannot auto-fix. Use this skill when linter outputs: - `csp-unsafe-inline-script` with message "Use of unsafe inline script" Trigger on: inline JavaScript in HTML files, script tags without src attribute, onclick/onload handlers in HTML. Also use when user mentions 'security headers', 'inline script warning', 'CSP policy', 'unsafe-inline', or 'Content-Security-Policy'. Provides guidance on moving inline scripts to external files for CSP compliance.
ユースケース
- ✓linterが`csp-unsafe-inline-script`を報告している
- ✓HTMLファイル内のインラインJavaScriptを修正する
- ✓CSP関連のキーワードが言及された場合
本文(日本語訳)
CSP コンプライアンス修正 - アンセーフインラインスクリプト
重要ルール
- インラインスクリプトの内容を絶対に削除しないこと。
必ず外部の
.jsファイルへ抽出し、インラインの<script>...</script>を<script src="filename.js"></script>に置き換えること。 些細な設定オブジェクト、デバッグフラグ、一見使われていないコードであっても、外部化が必要です。削除は機能的なリグレッションとなります。 - ファイル名は内容の目的に合った分かりやすい名前を使用すること
(例: 設定なら
appConfig.js、初期化ならinit.js)。
このスキルは、UI5 リンターが検出するものの、コードを外部ファイルへ再構成する必要があるため自動修正できない Content Security Policy(CSP)コンプライアンス違反を修正します。
対象リンタールール
| ルール ID | メッセージパターン | 重大度 | このスキルの対応 |
|---|---|---|---|
csp-unsafe-inline-script |
Use of unsafe inline script | Warning | 外部 JS ファイルへ移動 |
次のような場合に使用
リンター出力に以下のような表示がある場合に適用します:
index.html:15:5 warning Use of unsafe inline script csp-unsafe-inline-script
test.html:20:5 warning Use of unsafe inline script csp-unsafe-inline-script
背景: CSP が重要な理由
Content Security Policy(CSP)は以下を防ぐセキュリティ機能です:
- クロスサイトスクリプティング(XSS)攻撃
- データインジェクション攻撃
- 不正なスクリプト実行
インラインスクリプトは、HTML インジェクションに成功した攻撃者が悪意ある JavaScript も同時に注入できるため、危険とみなされます。
CSP 準拠のアプリは script-src 'self' を使用し、インラインスクリプトをブロックします。
ドキュメント: Content Security Policy
検出対象
リンターは以下の条件に該当する <script> タグにフラグを立てます:
src属性が ない かつ- インライン JavaScript の内容がある
フラグが立つ例:
<script>
console.log("Inline code"); <!-- フラグあり -->
</script>
<script type="text/javascript">
doSomething(); <!-- フラグあり -->
</script>
<script type="module">
import { foo } from './foo.js'; <!-- フラグあり -->
</script>
フラグが立たない例:
<script src="app.js"></script> <!-- 外部ファイル = OK -->
<script src="app.js">
// この内容はブラウザが無視する
</script>
<script type="text/xmldata">
<!-- JS 以外の MIME タイプ = OK -->
<data>...</data>
</script>
修正方針
すべてのインラインスクリプトに対する修正は共通です:
外部ファイルへ移動し、元の場所に <script src="..."> タグを置く。
インラインスクリプトの内容は保持されます — HTML 内ではなく、別の .js ファイルに記述するだけです。
インラインスクリプトの内容を削除せず、必ず外部化してください。
1. 基本的なインラインスクリプト → 外部ファイル
問題: HTML 内にインライン JavaScript がある。
<!-- 修正前 - index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My App</title>
<script>
window.myConfig = {
apiUrl: "/api/v1",
debug: true
};
</script>
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true">
</script>
<script>
sap.ui.require(["my/app/init"], function(init) {
init.start();
});
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
修正方針: インラインスクリプトを外部ファイルへ移動する。
<!-- 修正後 - index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My App</title>
<script src="config.js"></script>
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true"
data-sap-ui-on-init="module:my/app/init">
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
// config.js
window.myConfig = {
apiUrl: "/api/v1",
debug: true
};
// my/app/init.js
sap.ui.define([], function() {
"use strict";
return {
start: function() {
// 初期化コード
}
};
});
2. UI5 ブートストラップとインライン Init → data-sap-ui-on-init
問題: UI5 ブートストラップの後にインラインスクリプトがある。
<!-- 修正前 -->
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true"
data-sap-ui-resource-roots='{"my.app": "./"}'
data-sap-ui-compat-version="edge">
</script>
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.require([
"sap/m/Shell",
"sap/ui/core/ComponentContainer"
], function(Shell, ComponentContainer) {
new Shell({
app: new ComponentContainer({
name: "my.app",
async: true
})
}).placeAt("content");
});
});
</script>
修正方針: data-sap-ui-on-init 属性を使用する。
<!-- 修正後 - index.html -->
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true"
data-sap-ui-resource-roots='{"my.app": "./"}'
data-sap-ui-compat-version="edge"
data-sap-ui-on-init="module:my/app/init">
</script>
// webapp/init.js
sap.ui.define([
"sap/m/Shell",
"sap/ui/core/ComponentContainer"
], function(Shell, ComponentContainer) {
"use strict";
new Shell({
app: new ComponentContainer({
name: "my.app",
async: true
})
}).placeAt("content");
});
3. 設定データ → JSON またはモジュール
問題: インラインで設定オブジェクトが記述されている。
<!-- 修正前 -->
<script>
window.APP_CONFIG = {
apiEndpoint: "https://api.example.com",
features: {
darkMode: true,
analytics: false
}
};
</script>
修正方針 A: 実行時に読み込む外部 JSON ファイルを使用する。
<!-- 修正後 - オプション A: JSON ファイル -->
<script src="config.js"></script>
// config.js - JSON を読み込む
(function() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "config.json", false); // 設定用に同期取得
xhr.send();
window.APP_CONFIG = JSON.parse(xhr.responseText);
})();
// config.json
{
"apiEndpoint": "https://api.example.com",
"features": {
"darkMode": true,
"analytics": false
}
}
修正方針 B: UI5 モジュールで設定を管理する。
// my/app/config.js
sap.ui.define([], function() {
"use strict";
return {
apiEndpoint: "https://api.example.com",
features: {
darkMode: true,
analytics: false
}
};
});
// 他のモジュールでの使用例
sap.ui.define(["my/app/config"], function(config) {
console.log(config.apiEndpoint);
});
4. インラインイベントハンドラー → 外部スクリプト
問題: HTML 属性にインラインイベントハンドラーが記述されている。
<!-- 修正前 -->
<button onclick="handleClick()">Click me</button>
<img src="logo.png" onerror="handleError(this)">
<body onload="init()">
修正方針: イベントリスナーを使った外部スクリプトに置き換える。
<!-- 修正後 -->
<button id="myButton">Click me</button>
<img id="logo" src="logo.png">
<body>
// app.js
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("myButton").addEventListener("click", handleClick);
document.getElementById("logo").addEventListener("error", function() {
handleError(this);
});
init();
});
function handleClick() {
// クリックハンドラー
}
function handleError(element) {
// エラーハンドラー
}
function init() {
// 初期化
}
5. テスト HTML ファイル
問題: QUnit テストファイルにインラインスクリプトがある。
<!-- 修正前 - myTest.qunit.html -->
<!DOCTYPE html>
<html>
<head>
<script src="resources/sap-ui-core.js"
data-sap-ui-async="true">
</script>
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.require(["my/app/test/myTest"]);
});
</script>
</head>
<body>
<div id="qunit"></div>
</body>
</html>
修正方針: Test Starter を使用する(prefer-test-starter も同時に修正)。
<!-- 修正後 - myTest.qunit.html -->
<!DOCTYPE html>
<html>
<head>
<script
src="resources/sap/ui/test/starter/runTest.js"
data-sap-ui-testsuite="test-resources/my/app/test/testsuite.qunit">
</script>
</head>
<body>
<div id="qunit"></div>
</body>
</html>
6. 動的スクリプトコンテンツ
問題: スクリプトの内容がサーバーサイドで動的に生成される。
<!-- 修正前 -->
<script>
var userId = "<%= user.id %>"; // サーバーサイドテンプレート
var token = "<?php echo $token; ?>";
</script>
修正方針: data 属性または meta タグを使用する。
<!-- 修正後 -->
<meta name="user-id" content="<%= user.id %>">
<meta name="csrf-token" content="<?php echo $token; ?>">
<script src="app.js"></script>
// app.js
var userId = document.querySelector('meta[name="user-id"]').content;
var token = document.querySelector('meta[name="csrf-token"]').content;
実装手順
-
リンター出力からすべてのインラインスクリプトを特定する
-
各スクリプトを分類する:
- UI5 初期化 →
data-sap-ui-on-initを使用 - 設定 → 外部 JS ファイルへ
- イベントハンドラー →
addEventListenerを使った外部スクリプトへ - テストのボイラープレート → Test Starter を使用
- UI5 初期化 →
-
スクリプトの内容を格納する外部ファイルを作成する
-
HTML を更新して外部ファイルを参照させる
-
アプリケーションをテストして機能が維持されていることを確認する
よくあるパターン
| インラインパターン | CSP 準拠の解決策 |
|---|---|
<script>code</script> |
<script src="file.js"> |
data-sap-ui-on-init にインライン記述 |
data-sap-ui-on-init="module:path/to/init" |
onclick="fn()" |
element.addEventListener("click", fn) |
onerror="fn()" |
element.addEventListener("error", fn) |
onload="fn()" |
DOMContentLoaded イベントリスナー |
| サーバーレンダリングされた設定値 | <meta> タグ + JS での読み取り |
| QUnit インラインブートストラップ | Test Starter の runTest.js |
注意事項
- CSP コンプライアンスは Warning(エラーではない)です。環境によっては必須でない場合があります
data-sap-ui-on-init属性は AMD モジュール向けにmodule:path/to/module形式を受け付けます- サーバーレンダリングされた動的な値には
<meta>タグまたはdata-*属性を使用してください - テストファイルは CSP
原文(English)を表示
Fix CSP Compliance - Unsafe Inline Scripts
Key Rules
- NEVER delete inline script content. Always extract it to an external
.jsfile and replace the inline<script>...</script>with<script src="filename.js"></script>. Even trivial config objects, debug flags, or seemingly unused code must be externalized — removal is a functional regression. - File naming: use a descriptive name matching the content's purpose (e.g.,
appConfig.jsfor configuration,init.jsfor initialization).
This skill fixes Content Security Policy (CSP) compliance issues that the UI5 linter detects but cannot auto-fix because they require restructuring code into external files.
Linter Rule Handled
| Rule ID | Message Pattern | Severity | This Skill's Action |
|---|---|---|---|
csp-unsafe-inline-script |
Use of unsafe inline script | Warning | Move to external JS file |
When to Use
Apply this skill when you see linter output like:
index.html:15:5 warning Use of unsafe inline script csp-unsafe-inline-script
test.html:20:5 warning Use of unsafe inline script csp-unsafe-inline-script
Background: Why CSP Matters
Content Security Policy (CSP) is a security feature that helps prevent:
- Cross-Site Scripting (XSS) attacks
- Data injection attacks
- Unauthorized script execution
Inline scripts are considered unsafe because an attacker who manages to inject HTML can also inject malicious JavaScript. CSP-compliant apps use script-src 'self' which blocks inline scripts.
Documentation: Content Security Policy
Detection
The linter flags <script> tags that:
- Have NO
srcattribute AND - Have inline JavaScript content
Flagged:
<script>
console.log("Inline code"); <!-- Flagged -->
</script>
<script type="text/javascript">
doSomething(); <!-- Flagged -->
</script>
<script type="module">
import { foo } from './foo.js'; <!-- Flagged -->
</script>
Not flagged:
<script src="app.js"></script> <!-- External = OK -->
<script src="app.js">
// This content is ignored by browser anyway
</script>
<script type="text/xmldata">
<!-- Non-JS MIME type = OK -->
<data>...</data>
</script>
Fix Strategy
The fix for every inline script is the same: move it to an external file and add a <script src="..."> tag in its place. The content of the inline script is preserved — just in a separate .js file instead of inside the HTML. Never delete inline script content; always externalize it.
1. Basic Inline Script → External File
Problem: Inline JavaScript in HTML.
<!-- Before - index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My App</title>
<script>
window.myConfig = {
apiUrl: "/api/v1",
debug: true
};
</script>
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true">
</script>
<script>
sap.ui.require(["my/app/init"], function(init) {
init.start();
});
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
Fix Strategy: Move inline scripts to external files.
<!-- After - index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My App</title>
<script src="config.js"></script>
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true"
data-sap-ui-on-init="module:my/app/init">
</script>
</head>
<body class="sapUiBody" id="content">
</body>
</html>
// config.js
window.myConfig = {
apiUrl: "/api/v1",
debug: true
};
// my/app/init.js
sap.ui.define([], function() {
"use strict";
return {
start: function() {
// Initialization code
}
};
});
2. UI5 Bootstrap with Inline Init → data-sap-ui-on-init
Problem: Inline script after UI5 bootstrap.
<!-- Before -->
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true"
data-sap-ui-resource-roots='{"my.app": "./"}'
data-sap-ui-compat-version="edge">
</script>
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.require([
"sap/m/Shell",
"sap/ui/core/ComponentContainer"
], function(Shell, ComponentContainer) {
new Shell({
app: new ComponentContainer({
name: "my.app",
async: true
})
}).placeAt("content");
});
});
</script>
Fix Strategy: Use data-sap-ui-on-init attribute.
<!-- After - index.html -->
<script
id="sap-ui-bootstrap"
src="resources/sap-ui-core.js"
data-sap-ui-async="true"
data-sap-ui-resource-roots='{"my.app": "./"}'
data-sap-ui-compat-version="edge"
data-sap-ui-on-init="module:my/app/init">
</script>
// webapp/init.js
sap.ui.define([
"sap/m/Shell",
"sap/ui/core/ComponentContainer"
], function(Shell, ComponentContainer) {
"use strict";
new Shell({
app: new ComponentContainer({
name: "my.app",
async: true
})
}).placeAt("content");
});
3. Configuration Data → JSON or Module
Problem: Inline configuration object.
<!-- Before -->
<script>
window.APP_CONFIG = {
apiEndpoint: "https://api.example.com",
features: {
darkMode: true,
analytics: false
}
};
</script>
Fix Strategy A: External JSON file loaded at runtime.
<!-- After - Option A: JSON file -->
<script src="config.js"></script>
// config.js - loads JSON
(function() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "config.json", false); // Sync for config
xhr.send();
window.APP_CONFIG = JSON.parse(xhr.responseText);
})();
// config.json
{
"apiEndpoint": "https://api.example.com",
"features": {
"darkMode": true,
"analytics": false
}
}
Fix Strategy B: UI5 module with configuration.
// my/app/config.js
sap.ui.define([], function() {
"use strict";
return {
apiEndpoint: "https://api.example.com",
features: {
darkMode: true,
analytics: false
}
};
});
// Usage in other modules
sap.ui.define(["my/app/config"], function(config) {
console.log(config.apiEndpoint);
});
4. Inline Event Handlers → External Scripts
Problem: Inline event handlers in HTML attributes.
<!-- Before -->
<button onclick="handleClick()">Click me</button>
<img src="logo.png" onerror="handleError(this)">
<body onload="init()">
Fix Strategy: Use external script with event listeners.
<!-- After -->
<button id="myButton">Click me</button>
<img id="logo" src="logo.png">
<body>
// app.js
document.addEventListener("DOMContentLoaded", function() {
document.getElementById("myButton").addEventListener("click", handleClick);
document.getElementById("logo").addEventListener("error", function() {
handleError(this);
});
init();
});
function handleClick() {
// Click handler
}
function handleError(element) {
// Error handler
}
function init() {
// Initialization
}
5. Test HTML Files
Problem: QUnit test files with inline scripts.
<!-- Before - myTest.qunit.html -->
<!DOCTYPE html>
<html>
<head>
<script src="resources/sap-ui-core.js"
data-sap-ui-async="true">
</script>
<script>
sap.ui.getCore().attachInit(function() {
sap.ui.require(["my/app/test/myTest"]);
});
</script>
</head>
<body>
<div id="qunit"></div>
</body>
</html>
Fix Strategy: Use Test Starter (also fixes prefer-test-starter).
<!-- After - myTest.qunit.html -->
<!DOCTYPE html>
<html>
<head>
<script
src="resources/sap/ui/test/starter/runTest.js"
data-sap-ui-testsuite="test-resources/my/app/test/testsuite.qunit">
</script>
</head>
<body>
<div id="qunit"></div>
</body>
</html>
6. Dynamic Script Content
Problem: Script content generated dynamically.
<!-- Before -->
<script>
var userId = "<%= user.id %>"; // Server-side template
var token = "<?php echo $token; ?>";
</script>
Fix Strategy: Use data attributes or meta tags.
<!-- After -->
<meta name="user-id" content="<%= user.id %>">
<meta name="csrf-token" content="<?php echo $token; ?>">
<script src="app.js"></script>
// app.js
var userId = document.querySelector('meta[name="user-id"]').content;
var token = document.querySelector('meta[name="csrf-token"]').content;
Implementation Steps
-
Identify all inline scripts from linter output
-
Categorize each script:
- UI5 initialization → Use
data-sap-ui-on-init - Configuration → External JS file
- Event handlers → External script with
addEventListener - Test boilerplate → Use Test Starter
- UI5 initialization → Use
-
Create external files for the script content
-
Update HTML to reference external files
-
Test the application to ensure functionality is preserved
Common Patterns
| Inline Pattern | CSP-Compliant Solution |
|---|---|
<script>code</script> |
<script src="file.js"> |
data-sap-ui-on-init with inline |
data-sap-ui-on-init="module:path/to/init" |
onclick="fn()" |
element.addEventListener("click", fn) |
onerror="fn()" |
element.addEventListener("error", fn) |
onload="fn()" |
DOMContentLoaded event listener |
| Server-rendered config | <meta> tags + JS reader |
| QUnit inline bootstrap | Test Starter runTest.js |
Notes
- CSP compliance is a warning (not error) because some environments may not require it
- The
data-sap-ui-on-initattribute acceptsmodule:path/to/moduleformat for AMD modules - For server-rendered dynamic values, use
<meta>tags ordata-*attributes - Test files should use Test Starter for both CSP compliance and best practices
- Some third-party libraries may require CSP adjustments - check their documentation
- JSONP callbacks may need special handling in CSP configurations
原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。