UI5 リンター(コード品質チェック機能)が報告するが自動修正できないコンテンツセキュリティポリシー(CSP:ウェブページの安全性を守るルール)への対応問題を解決します。 次のような場合に使用してください: - リンターが「Use of unsafe inline script(安全でないインラインスクリプトの使用)」というメッセージで「csp-unsafe-inline-script」エラーを出力する場合 トリガー条件: - HTMLファイル内に直接書かれたJavaScript(インラインスクリプト) - src属性のない script タグ - HTMLに含まれた onclick/onload ハンドラー また、ユーザーが「セキュリティヘッダー」「インラインスクリプト警告」「CSPポリシー」「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.
.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
Content Security Policy(CSP)は以下を防ぐセキュリティ機能です:
インラインスクリプトは、HTML インジェクションに成功した攻撃者が悪意ある JavaScript も同時に注入できるため、危険とみなされます。
CSP 準拠のアプリは script-src 'self' を使用し、インラインスクリプトをブロックします。
ドキュメント: Content Security Policy
リンターは以下の条件に該当する <script> タグにフラグを立てます:
src 属性が ない かつフラグが立つ例:
<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 ファイルに記述するだけです。
インラインスクリプトの内容を削除せず、必ず外部化してください。
問題: 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() {
// 初期化コード
}
};
});
問題: 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");
});
問題: インラインで設定オブジェクトが記述されている。
<!-- 修正前 -->
<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);
});
問題: 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() {
// 初期化
}
問題: 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>
問題: スクリプトの内容がサーバーサイドで動的に生成される。
<!-- 修正前 -->
<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;
リンター出力からすべてのインラインスクリプトを特定する
各スクリプトを分類する:
data-sap-ui-on-init を使用addEventListener を使った外部スクリプトへスクリプトの内容を格納する外部ファイルを作成する
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 |
data-sap-ui-on-init 属性は AMD モジュール向けに module:path/to/module 形式を受け付けます<meta> タグまたは data-* 属性を使用してください.js file 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.appConfig.js for configuration, init.js for 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.
| Rule ID | Message Pattern | Severity | This Skill's Action |
|---|---|---|---|
csp-unsafe-inline-script |
Use of unsafe inline script | Warning | Move to external JS file |
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
Content Security Policy (CSP) is a security feature that helps prevent:
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
The linter flags <script> tags that:
src attribute ANDFlagged:
<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>
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.
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
}
};
});
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");
});
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);
});
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
}
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>
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;
Identify all inline scripts from linter output
Categorize each script:
data-sap-ui-on-initaddEventListenerCreate external files for the script content
Update HTML to reference external files
Test the application to ensure functionality is preserved
| 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 |
data-sap-ui-on-init attribute accepts module:path/to/module format for AMD modules<meta> tags or data-* attributes原文・著作権は Anthropic および各プラグイン作者に帰属します。日本語訳は Claude API による自動翻訳です。