-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
128 lines (109 loc) · 4.71 KB
/
script.js
File metadata and controls
128 lines (109 loc) · 4.71 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
(() => {
const root = document.documentElement;
const langButtons = document.querySelectorAll('.lang-btn');
const translatable = document.querySelectorAll('[data-i18n-jp][data-i18n-en]');
const nav = document.querySelector('.main-nav');
const menuBtn = document.querySelector('.menu-btn');
const applyLanguage = (lang) => {
root.setAttribute('lang', lang === 'en' ? 'en' : 'ja');
translatable.forEach((node) => {
const key = lang === 'en' ? 'i18nEn' : 'i18nJp';
const value = node.dataset[key];
if (typeof value === 'string') node.textContent = value;
});
langButtons.forEach((btn) => {
btn.classList.toggle('is-active', btn.dataset.lang === lang);
btn.setAttribute('aria-pressed', String(btn.dataset.lang === lang));
});
localStorage.setItem('lang-pref', lang);
};
langButtons.forEach((btn) => {
btn.addEventListener('click', () => applyLanguage(btn.dataset.lang || 'ja'));
});
const savedLang = localStorage.getItem('lang-pref') || 'ja';
applyLanguage(savedLang);
if (menuBtn && nav) {
menuBtn.addEventListener('click', () => {
const opened = nav.classList.toggle('is-open');
menuBtn.setAttribute('aria-expanded', String(opened));
});
nav.querySelectorAll('a').forEach((link) => {
link.addEventListener('click', () => {
nav.classList.remove('is-open');
menuBtn.setAttribute('aria-expanded', 'false');
});
});
}
const revealNodes = document.querySelectorAll('.reveal');
if ('IntersectionObserver' in window) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
entry.target.classList.add('is-visible');
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.18 }
);
revealNodes.forEach((node) => observer.observe(node));
} else {
revealNodes.forEach((node) => node.classList.add('is-visible'));
}
const scanBtn = document.getElementById('scan-btn');
const promptInput = document.getElementById('risk-prompt');
const riskFill = document.querySelector('.risk-fill');
const riskLabel = document.querySelector('.risk-label');
const riskReason = document.querySelector('.risk-reason');
if (scanBtn && promptInput && riskFill && riskLabel && riskReason) {
const scoreText = document.createElement('div');
scoreText.className = 'small';
scoreText.style.marginTop = '0.5rem';
riskReason.before(scoreText);
scanBtn.addEventListener('click', () => {
const text = promptInput.value.trim();
if (!text) {
riskLabel.textContent = '入力待ち';
riskReason.textContent = 'プロンプトを入力してから診断してください。';
riskFill.style.width = '0%';
riskFill.style.background = 'linear-gradient(90deg, #4fc3ff, #58ffe0)';
scoreText.textContent = 'Risk Score: -- / 100';
return;
}
const flags = [
{ keyword: /ignore|bypass|override|無視|回避|権限昇格/i, weight: 30, reason: '安全ポリシー回避を示す語句' },
{ keyword: /password|credential|token|秘密鍵|社外秘/i, weight: 28, reason: '機密情報取得につながる語句' },
{ keyword: /system prompt|内部指示|開発者メッセージ|prompt leak/i, weight: 24, reason: 'プロンプトリーク誘発の可能性' },
{ keyword: /download|execute|shell|script|curl|wget/i, weight: 18, reason: '外部実行・取得系コマンドの要求' },
{ keyword: /all data|全部出力|全件|データベース/i, weight: 16, reason: '過剰なデータ抽出要求' }
];
let score = 12;
const hitReasons = [];
flags.forEach((flag) => {
if (flag.keyword.test(text)) {
score += flag.weight;
hitReasons.push(flag.reason);
}
});
score += Math.min(20, Math.max(0, text.length - 100) / 8);
score = Math.min(99, Math.round(score));
let status = 'Low';
let color = 'linear-gradient(90deg, #2ed8b6, #53ffe1)';
if (score >= 70) {
status = 'High';
color = 'linear-gradient(90deg, #ff667a, #ff9b72)';
} else if (score >= 40) {
status = 'Medium';
color = 'linear-gradient(90deg, #ffd26f, #ff8e5f)';
}
riskFill.style.width = `${score}%`;
riskFill.style.background = color;
riskLabel.textContent = `判定: ${status}`;
scoreText.textContent = `Risk Score: ${score} / 100`;
riskReason.textContent = hitReasons.length
? `検出理由: ${hitReasons.join(' / ')}`
: '検出理由: 重大シグナルは未検出ですが、運用時はログ監視と最小権限設計を推奨します。';
});
}
})();