claude-skills/

Anthropic公式スキル・プラグインの日本語ディレクトリ

last sync 22h ago
スキルOfficialdevelopment

🔒fix-csp-compliance

プラグイン
ui5-modernization

説明

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 コンプライアンス修正 - アンセーフインラインスクリプト

重要ルール

  1. インラインスクリプトの内容を絶対に削除しないこと。 必ず外部の .js ファイルへ抽出し、インラインの <script>...</script><script src="filename.js"></script> に置き換えること。 些細な設定オブジェクト、デバッグフラグ、一見使われていないコードであっても、外部化が必要です。削除は機能的なリグレッションとなります。
  2. ファイル名は内容の目的に合った分かりやすい名前を使用すること (例: 設定なら 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;

実装手順

  1. リンター出力からすべてのインラインスクリプトを特定する

  2. 各スクリプトを分類する:

    • UI5 初期化 → data-sap-ui-on-init を使用
    • 設定 → 外部 JS ファイルへ
    • イベントハンドラー → addEventListener を使った外部スクリプトへ
    • テストのボイラープレート → Test Starter を使用
  3. スクリプトの内容を格納する外部ファイルを作成する

  4. HTML を更新して外部ファイルを参照させる

  5. アプリケーションをテストして機能が維持されていることを確認する


よくあるパターン

インラインパターン 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

  1. NEVER delete inline script content. Always extract it to an external .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.
  2. File naming: use a descriptive name matching the content's purpose (e.g., 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.

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 src attribute 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

  1. Identify all inline scripts from linter output

  2. 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
  3. Create external files for the script content

  4. Update HTML to reference external files

  5. 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-init attribute accepts module:path/to/module format for AMD modules
  • For server-rendered dynamic values, use <meta> tags or data-* 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 による自動翻訳です。