元宇品の散歩

元宇品の海岸でキクと
元宇品の森を歩く

建設アシストのコードのセキュリティ対策として、CSP(Content Security Policy)でunsafe-evalを禁止した結果、Alpine.js の式評価がブロックされ動作不能になった問題を解決します

1.パッケージ切り替え・ビルド設定

package.json / resources/js/app.js

変更内容詳細
alpinejs → @alpinejs/cspに差し替えnpm install @alpinejs/cspを実行、app.jsのインポートを変更
グローバル Alpine コンポーネントを app.jsに集約alpine:initイベント内で Alpine.data()を登録

app.jsに登録したグローバルコンポーネント:

コンポーネント名用途
scheduleInitModal(chartType, switchUrl)工程表 初期選択モーダル
flashMessageプロフィール保存後フラッシュメッセージ(2秒フェードアウト)
dropdownMenuナビゲーション ドロップダウン
laravelModal(initialShow, modalName, focusable)Breeze 標準モーダル(フォーカストラップ付き)

2.変更ファイル一覧(12ファイル)

2-1. resources/js/app.js
  • インポートをalpinejs → @alpinejs/cspに変更
  • alpine:initブロックに 4 つのグローバルコンポーネントを登録
2-2. resources/views/company_admin/index.blade.php
  • 着手前に対応済み(本作業の起点)
  • x-data=”companyAdmin”形式で Alpine.data 登録済み
2-3. resources/views/schedules/barchart.blade.php

barChart() 関数に CSP ヘルパーメソッド 32 件を追加。

変更前(HTML)変更後
@click=”viewMode=’edit’; renderAll()”@click=”switchToEdit()”
:style=”viewMode===’edit’ ? … : …”:style=”viewModeStyle(‘edit’)”
:style=”mode===’DRAW_BAR’ ? … : …”:style=”modeDrawBarStyle()”
@input=”setZoom($event.target.value/100)”@input=”setZoomFromInput($event)”
x-text=”Math.round(zoom*100)+’%'”x-text=”zoomText()”
:disabled=”!scheduleId”:disabled=”scheduleNotSelected()”
:style=”`width:${canvasWidth}px…`”:style=”hscrollStyle()”
:style=”`left:${ctxMenu.x}px…`”:style=”ctxMenuPosStyle()”
x-text=”ctxMenu.type===’bar’ ? … : …”x-text=”ctxMenuTitle()”
x-text=”barModal.startDate + ‘ → ‘ + …”x-text=”barStartEndText()”
:disabled=”!modalBasisTotal()”:disabled=”applyBasisDisabled()”
:class=”barModal.pattern === p.v ? …”:class=”patternBtnClass(p.v)”
x-text=”annotModal.isConnMode ? …”x-text=”annotModalTitle()”
@click=”annotModal.bold=!annotModal.bold”@click=”toggleAnnotBold()”
:style=”cell && cell.isExtra ? … : …”(5段ネスト):style=”calCellStyle(cell)”
x-show=”createModal.extraHolidays.length > 0″x-show=”hasExtraHolidays()”
x-text=”createModal.calendarYear + ‘年 ‘ + …”x-text=”calendarMonthTitle()”
2-7. resources/views/components/schedule-init-modal.blade.php

scheduleInitModal(chartType, switchUrl)をapp.js`に登録。savedSchedules /openCreateModal() /openLoadModal()`は Alpine プロキシ経由で親スコープ(barChart / networkSchedule)から参照。

変更前変更後
x-data=”{ selectedType:{{ $chartType }}’ }” |x-data=”scheduleInitModal(‘{{ $chartType }}, {{ $switchUrl }})”
@click=”selectedType ===…&& savedSchedules.length === 0 ? …”:class=”loadBtnClass()”
x-text=”(+savedSchedules.length+件)”x-text=”savedCountText()”
2-10. resources/views/audits/findings/edit.blade.php

findingsEditor()` にヘルパーメソッド 10 件を追加。PHP@foreach でベイクインされた{ $idx }} /{{ $sys->id }} をメソッド引数として渡す形式に変換。

変更前変更後
:class=”{{ $idx }} === activeTab ? … : …”:class=”tabBtnClass({{ $idx }})”
x-text=”‘(‘ + (findingsByIso[{{ $sys->id }}]?.length || 0) + ‘)'”x-text=”isoCountText({{ $sys->id }})”
x-show=”activeTab === {{ $idx }}”x-show=”isActiveTabIdx({{ $idx }})”
:key=”‘{{ $sys->id }}-‘ + (f.id || (‘new-‘ + fi))”:key=”findingKey({{ $sys->id }}, f, fi)”
x-if=”(findingsByIso[{{ $sys->id }}]?.length || 0) === 0″x-if=”isoHasNoFindings({{ $sys->id }})”
x-text=”modal.isNew ? ‘…’ : ‘…’ + (modal.index + 1) + ‘…'”x-text=”modalHeaderText()”
x-text=”c.clause_no + (c.short_label ? ‘ ’ + c.short_label : ”)”x-text=”clauseOptionText(c)”
x-text=”modalClauseSummary() || ‘…'”x-text=”modalClauseSummaryText()”
x-show=”modal.form.finding_rank && …startsWith(‘nonconformity’)”x-show=”isNonconformity()”
2-12. resources/views/profile/partials/update-password-form.blade.php
2-13. resources/views/profile/partials/update-profile-information-form.blade.php

両ファイル同一パターン。flashMessageコンポーネントを app.jsに登録して対応。

変更前変更後
x-data=”{ show: true }”x-data=”flashMessage”
x-init=”setTimeout(() => show = false, 2000)”(削除 — init()メソッド内に移行)

4.確認事項

全変更後 npm run buildが成功(エラーなし)
@alpinejs/csp ^3.15.12をpackage.json のdependencies に追加済み
標準 alpinejsはdevDependenciesに残存(他依存なければ削除可)

2-4. resources/views/schedules/network.blade.php

networkSchedule() 関数に CSP ヘルパーメソッド 39 件を追加。barchart と共通の変換に加え以下を対応。

変更前変更後
@change=”renumberNodes(); renderAll()”@change=”renumberAndRender()”
:class=”mode===’IDLE’ ? ‘text-gray-300: …”:class=”hintBarClass()”
:style=”modal.lineType===’solid ? …”:style=”lineTypeStyle(‘solid’)”
:style=”modal.arrowShape===TL ? …”:style=”arrowShapeStyle(TL)”` |(6種類)
x-text=”fmtDate(modal.startDate)+’ → ‘+calcEndDate()”x-text=”arrowDateRange()”
x-text=”modalCeilDays() ? … + ‘日’ : ‘-'”x-text=”ceilDaysText()”
x-text=”modalBasisTotal() ? … + ‘日’ : ‘-'”x-text=”basisTotalText()”
2-5. resources/views/components/modal.blade.php

Breeze 標準モーダルコンポーネントをLaravelModalに完全移行。

変更前変更後
x-data=”{show: @js($show), focusables() {…}, …}”|init()`としてapp.js のlaravelModal`内に移行x-data=”laravelModal(@js($show), ‘{{ $name }}’, …)”
x-init=”$watch(‘show’, value => {…})”x-on:open-modal.window=”… ? show = true : null”
x-on:open-modal.window=”handleOpenModal($event)”x-on:keydown.tab.prevent=”$event.shiftKey || …”
x-on:keydown.tab.prevent=”onTab($event)”x-on:click=”show = false” |x-on:click=”closeModal()”
2-6. resources/views/components/dropdown.blade.php
変更前変更後
x-data=”{ open: false }”x-data=”dropdownMenu”
@click.outside=”open = false”@click.outside=”closeMenu()”
@click=”open = ! open”@click=”toggleMenu()”
2-8. resources/views/admin/users/log_modal.blade.php

logModal() /changeLogModal()は既に function 形式のためx-data変更不要。各関数にヘルパーメソッドを追加。

追加メソッド変換した式
logHeaderText()userName + のログイン・ログアウト履歴
noLogs()!loading && logs.length === 0
hasLogs()!loading && logs.length > 0
showLimit()logs.length >= 100
logoutTimeClass(log)log.logout_time === — ? … : …
changeHeaderText()userName + ‘ のプロフィール変更履歴’
2-9. resources/views/audits/checklists/edit.blade.php

checklistEditor() にヘルパーメソッド 8 件を追加。

変更前変更後
x-if=”tabs.length > 0″x-if=”hasTabs()”
x-if=”tabs.length === 0″x-if=”noTabs()”
:class=”ti === activeTab ? … : …”:class=”tabClass(ti)”
x-text=”‘(‘ + (itemsByTab[t]?.length || 0) + ‘)'”x-text=”tabCountText(t)”
:key=”‘pane-‘ + t”:key=”tabPaneKey(t)”
x-show=”ti === activeTab”x-show=”isActiveTab(ti)”
x-show=”(itemsByTab[t]?.length || 0) === 0″x-show=”tabHasNoItems(t)”
x-text=”idx + 1″x-text=”rowNumber(idx)”
2-11. resources/views/audits/reports/show.blade.php

<script> ブロックにauditReport(initialType) 関数を新規追加。

変更前変更後
x-data=”{ targetType:{{ $report->target_type ?:department’ }}’ }”x-data=”auditReport(‘{{ $report->target_type ?:department’ }}’)”
x-show=”targetType===department”x-show=”isDepartment()”
x-show=”targetType===project'”x-show=”isProject()”
x-if=”targetType===’department'”x-if=”isDepartment()”
x-if=”targetType===’project'”x-if=”isProject()”

3.変換ルール まとめ

パターン変換方針
x-data=”{ key: val }”` インラインオブジェクトAlpine.data(‘name’, …) をapp.jsまたは<script> ブロックに登録しx-data=”name” または x-data=”name(params)”に変更
:style/:class 三項演算子データオブジェクトにメソッドを追加し :style=”methodName()”に変換
@click=”a = !a” 代入式toggleXxx() メソッドに変換
@input=”fn($event.target.value/100)”` 算術式ラッパーメソッド setXxxFromInput($event)に変換
x-text=”a + ‘text’ + b”` 文字列結合xxxText()` メソッドに変換
x-show=”a && b.length > 0″ 複合論理式hasXxx()/noXxx() メソッドに変換
x-if=”arr.length === 0″ 比較式hasXxx()/noXxx() メソッドに変換
:style=”`left:${x}px`”テンプレートリテラルxxxStyle() メソッドに変換(最優先・確実にNGのため)
x-init=”setTimeout(() => …, n)”` アロー関数init()` メソッド内に移行

ブックマークする パーマリンク.

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です