-
Notifications
You must be signed in to change notification settings - Fork 0
update #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
update #8
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -11,9 +11,28 @@ export class CourseUI { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| courseList: document.getElementById('course-list'), | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| appLayout: document.querySelector('.app-layout') | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // UIOptimizerを初期化 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.uiOptimizer = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.initializeUIOptimizer(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bindEvents(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * UIOptimizerを初期化 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async initializeUIOptimizer() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const { UIOptimizer } = await import('./ui-optimizer.js'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.uiOptimizer = new UIOptimizer(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.uiOptimizer.initialize(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log('CourseUI: UIOptimizerが初期化されました'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.warn('UIOptimizerの読み込みに失敗しました:', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * イベントリスナーを設定 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -47,19 +66,28 @@ export class CourseUI { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * コース一覧を描画 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * コース一覧を描画(最適化版) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| renderCourseList(courses) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!courses || courses.length === 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.elements.courseList.innerHTML = '<p>利用可能なコースがありません</p>'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| const courseCards = courses.map(course => this.createCourseCard(course)).join(''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.elements.courseList.innerHTML = courseCards; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // コース選択ボタンのイベントリスナーを設定 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bindCourseSelectionEvents(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // UIOptimizerが利用可能な場合は最適化された更新を使用 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (this.uiOptimizer) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.uiOptimizer.updateCourseSelection(courses, 'high'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // イベントリスナーを遅延設定 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| setTimeout(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bindCourseSelectionEvents(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, 50); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // フォールバック: 従来の方法 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const courseCards = courses.map(course => this.createCourseCard(course)).join(''); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.elements.courseList.innerHTML = courseCards; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Caution Description: A potential code injection vulnerability has been detected, where untrusted input is passed to a method that may execute arbitrary code. This issue allows attackers to inject and execute arbitrary code within the application, which could lead to unauthorized access to sensitive data or other malicious actions. To mitigate this, ensure that all user-supplied input is properly sanitized and validated before being processed. Avoid passing untrusted input to methods like eval, render etc, that can execute arbitrary code. Where possible, use safer alternatives such as parameterized queries or more controlled methods for handling user input. Learn more Severity: Critical There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The fix replaces the direct assignment to innerHTML with a safer approach using textContent and DOM manipulation to prevent potential XSS attacks. It creates a temporary div element to parse the HTML string and then appends it to the courseList element.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.bindCourseSelectionEvents(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -185,21 +213,25 @@ export class CourseUI { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * コースを選択して学習を開始 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * コースを選択して学習を開始(最適化版) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| async selectCourse(courseId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ローディング状態を表示 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.showCourseLoadingState(courseId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // コースを取得 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const course = this.courseManager.getCourse(courseId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 準備中のコースかチェック | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (course.status === 'coming_soon') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert('このコースは現在準備中です。近日公開予定です。'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.hideCourseLoadingState(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // コースを選択 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const selectedCourse = this.courseManager.selectCourse(courseId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // コースを選択(遅延読み込み対応) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const selectedCourse = await this.courseManager.selectCourse(courseId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(`コースを選択しました: ${selectedCourse.title}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // GameEngineにコースを設定 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -220,17 +252,52 @@ export class CourseUI { | |||||||||||||||||||||||||||||||||||||||||||||||||||
| window.progressUI.onCourseSelected(course); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| // チャレンジを更新 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // チャレンジを更新(非同期で実行) | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (window.uiController && typeof window.uiController.updateChallenge === 'function') { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.uiController.updateChallenge(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| // UI更新を次のフレームで実行 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestAnimationFrame(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| window.uiController.updateChallenge(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.hideCourseLoadingState(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (error) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.error('コース選択エラー:', error); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.hideCourseLoadingState(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| alert(`コース選択に失敗しました: ${error.message}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * コース読み込み状態を表示 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| showCourseLoadingState(courseId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const courseCard = document.querySelector(`[data-course-id="${courseId}"]`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (courseCard) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const selectBtn = courseCard.querySelector('.course-select-btn'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (selectBtn) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectBtn.disabled = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectBtn.textContent = '読み込み中...'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * コース読み込み状態を非表示 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| hideCourseLoadingState() { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const selectBtns = document.querySelectorAll('.course-select-btn'); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| selectBtns.forEach(btn => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| btn.disabled = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const courseId = btn.dataset.courseId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const course = this.courseManager.getCourse(courseId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const progress = this.courseManager.getCourseProgress(courseId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| const isStarted = progress && progress.completedLessons.length > 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| btn.textContent = isStarted ? '続きから学習' : 'コースを開始'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| * コース詳細情報を表示 | ||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Warning
Description: Log Injection occurs when untrusted user input is directly written to log files without proper sanitization. This can allow attackers to manipulate log entries, potentially leading to security issues like log forging or cross-site scripting. To prevent this, always sanitize user input using
encodeURIComponent()orDOMPurify.sanitize()before logging. Learn more - https://cwe.mitre.org/data/definitions/117.htmlSeverity: High
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fix uses DOMPurify.sanitize() to sanitize the courseId before logging it, preventing potential log injection attacks by removing any malicious content from the user input.