From cf10f757a8631c6fbb4564397ee4a7cd19f0e07e Mon Sep 17 00:00:00 2001 From: posimai Date: Tue, 21 Apr 2026 08:24:33 +0900 Subject: [PATCH] =?UTF-8?q?fix(pc-audit):=20=E7=AE=A1=E7=90=86=E8=80=85?= =?UTF-8?q?=E5=88=A4=E5=AE=9A=E3=82=923=E6=AE=B5=E9=9A=8E=E3=81=AB?= =?UTF-8?q?=E5=BC=B7=E5=8C=96=E3=80=81viewer=20=E3=82=923=E3=82=AB?= =?UTF-8?q?=E3=83=A9=E3=83=A0=E3=83=AC=E3=82=A4=E3=82=A2=E3=82=A6=E3=83=88?= =?UTF-8?q?=E3=81=AB=E5=A4=89=E6=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PS1: IsInRole -> SID string 比較 -> net localgroup の3段階で判定。 UAC トークンフィルタリング環境でも管理者ユーザーを正しく検出する。 viewer: 左寄せ3カラム(概要/差分 | チェック項目 | ポート一覧)。 スマホは1カラムに自動切替。一時ポート(49152-65535)に「正常」説明を追加。 Co-Authored-By: Claude Sonnet 4.6 --- tools/pc-audit/Invoke-PcAudit.ps1 | 21 +- tools/pc-audit/report-viewer.html | 358 ++++++++++++++++-------------- 2 files changed, 201 insertions(+), 178 deletions(-) diff --git a/tools/pc-audit/Invoke-PcAudit.ps1 b/tools/pc-audit/Invoke-PcAudit.ps1 index e02570ae..f9843871 100644 --- a/tools/pc-audit/Invoke-PcAudit.ps1 +++ b/tools/pc-audit/Invoke-PcAudit.ps1 @@ -48,16 +48,23 @@ if (Test-Path -LiteralPath $latestPathForPrev) { } function Test-IsAdmin { - # IsInRole checks the *current token* (elevated or not under UAC). - # We also check group membership via SID so non-elevated admin sessions are detected. + # Check 1: current process token is elevated (Run as Administrator) $id = [Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object Security.Principal.WindowsPrincipal($id) if ($p.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { return $true } - # Fallback: check if the user's identity is a member of BUILTIN\Administrators (S-1-5-32-544) - $adminSid = New-Object Security.Principal.SecurityIdentifier("S-1-5-32-544") - foreach ($g in $id.Groups) { - if ($g.Equals($adminSid)) { return $true } - } + # Check 2: user is a member of BUILTIN\Administrators group (SID S-1-5-32-544). + # Under UAC the token is filtered so IsInRole returns false even for admin group members. + # Comparing .Value (string form of SID) is reliable regardless of token filtering. + $adminSidValue = "S-1-5-32-544" + if ($id.Groups.Value -contains $adminSidValue) { return $true } + # Check 3: fallback via net localgroup (catches edge cases where token groups differ) + try { + $currentUser = $env:USERNAME + $members = & net localgroup Administrators 2>$null + foreach ($line in $members) { + if ($line.Trim() -eq $currentUser) { return $true } + } + } catch {} return $false } diff --git a/tools/pc-audit/report-viewer.html b/tools/pc-audit/report-viewer.html index d31b6409..dafb80e4 100644 --- a/tools/pc-audit/report-viewer.html +++ b/tools/pc-audit/report-viewer.html @@ -14,8 +14,6 @@ --text2: #9CA3AF; --text3: #6B7280; --accent: #6EE7B7; - --accent-dim: rgba(110,231,183,0.08); - --accent-border: rgba(110,231,183,0.2); --warn-color: #F59E0B; --warn-dim: rgba(245,158,11,0.08); --warn-border: rgba(245,158,11,0.2); @@ -36,26 +34,33 @@ line-height: 1.6; min-height: 100vh; } - .page { max-width: 52rem; margin: 0 auto; padding: 2rem 1.25rem 4rem; } - /* Header */ + /* Layout */ + .page { max-width: 1280px; padding: 2rem 1.5rem 4rem; } + .header { margin-bottom: 2rem; } .header h1 { font-size: 1.1rem; font-weight: 600; letter-spacing: 0.05em; color: var(--text2); } .header h1 span { color: var(--accent); } .privacy-note { margin-top: 0.5rem; - font-size: 0.8rem; + font-size: 0.78rem; color: var(--text3); display: flex; align-items: center; gap: 0.5rem; } - .privacy-dot { - display: inline-block; - width: 6px; height: 6px; - border-radius: 50%; - background: var(--accent); - flex-shrink: 0; + .privacy-dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex-shrink: 0; } + + /* 3-column grid — collapses to 1 col on mobile */ + .grid3 { + display: grid; + grid-template-columns: 280px 1fr 1fr; + gap: 1.25rem; + align-items: start; + } + @media (max-width: 860px) { + .grid3 { grid-template-columns: 1fr; } + .page { padding: 1rem 1rem 3rem; } } /* Upload area */ @@ -63,155 +68,99 @@ background: var(--surface); border: 1px dashed var(--border); border-radius: var(--radius); - padding: 1.75rem 1.5rem; - margin-bottom: 2rem; + padding: 1.25rem; + margin-bottom: 1rem; } - .upload-label { font-size: 0.85rem; font-weight: 600; color: var(--text2); margin-bottom: 0.5rem; display: block; } + .upload-label { font-size: 0.82rem; font-weight: 600; color: var(--text2); margin-bottom: 0.4rem; display: block; } .upload-path { - font-size: 0.78rem; - color: var(--text3); - background: var(--surface2); - padding: 0.3rem 0.6rem; - border-radius: var(--radius-sm); - font-family: monospace; - display: inline-block; - margin-bottom: 0.75rem; - } - input[type="file"] { - font-size: 0.85rem; - color: var(--text2); - cursor: pointer; + font-size: 0.75rem; color: var(--text3); background: var(--surface2); + padding: 0.25rem 0.5rem; border-radius: var(--radius-sm); font-family: monospace; + display: inline-block; margin-bottom: 0.6rem; } + input[type="file"] { font-size: 0.82rem; color: var(--text2); cursor: pointer; max-width: 100%; } input[type="file"]::file-selector-button { - background: var(--surface2); - color: var(--text); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 0.35rem 0.9rem; - font-size: 0.8rem; - cursor: pointer; - margin-right: 0.75rem; + background: var(--surface2); color: var(--text); border: 1px solid var(--border); + border-radius: var(--radius-sm); padding: 0.3rem 0.75rem; font-size: 0.78rem; + cursor: pointer; margin-right: 0.5rem; margin-bottom: 0.25rem; } /* Section label */ .section-title { - font-size: 0.68rem; - font-weight: 700; - letter-spacing: 0.1em; - text-transform: uppercase; - color: var(--text3); - margin: 2rem 0 0.75rem; + font-size: 0.65rem; font-weight: 700; letter-spacing: 0.1em; + text-transform: uppercase; color: var(--text3); margin: 0 0 0.6rem; } - /* Summary grid */ - .summary-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); - gap: 0.75rem; - margin-bottom: 0.5rem; - } + /* Metric grid */ + .summary-grid { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 0.75rem; } .metric { - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - padding: 0.85rem 1rem; + background: var(--surface); border: 1px solid var(--border); + border-radius: var(--radius-sm); padding: 0.7rem 0.9rem; } - .metric-label { font-size: 0.72rem; color: var(--text3); margin-bottom: 0.2rem; } - .metric-value { font-size: 0.95rem; font-weight: 600; color: var(--text); word-break: break-all; } + .metric-label { font-size: 0.7rem; color: var(--text3); margin-bottom: 0.15rem; } + .metric-value { font-size: 0.9rem; font-weight: 600; color: var(--text); word-break: break-all; } /* Diff card */ - .diff-card { - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 1.25rem 1.5rem; - } - .diff-card-title { font-size: 0.82rem; font-weight: 600; color: var(--text2); margin-bottom: 0.75rem; } - .diff-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr)); gap: 0.5rem; } + .diff-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 1rem 1.1rem; } + .diff-card-title { font-size: 0.8rem; font-weight: 600; color: var(--text2); margin-bottom: 0.6rem; } + .diff-col { display: flex; flex-direction: column; gap: 0.4rem; } - /* Guidance cards — no left border */ + /* Cards */ .card { - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 1.25rem 1.5rem; - margin-bottom: 0.75rem; + background: var(--surface); border: 1px solid var(--border); + border-radius: var(--radius); padding: 1rem 1.1rem; margin-bottom: 0.6rem; } .card.warn { background: var(--warn-dim); border-color: var(--warn-border); } .card.ok { background: var(--ok-dim); border-color: var(--ok-border); } .card.info { background: var(--info-dim); border-color: var(--info-border); } - - .card-header { display: flex; align-items: flex-start; gap: 0.6rem; margin-bottom: 0.6rem; } + .card-header { display: flex; align-items: flex-start; gap: 0.5rem; margin-bottom: 0.5rem; } .badge { - font-size: 0.65rem; - font-weight: 700; - letter-spacing: 0.06em; - padding: 0.2rem 0.55rem; - border-radius: 99px; - white-space: nowrap; - flex-shrink: 0; - margin-top: 0.15rem; + font-size: 0.63rem; font-weight: 700; letter-spacing: 0.06em; + padding: 0.18rem 0.5rem; border-radius: 99px; white-space: nowrap; + flex-shrink: 0; margin-top: 0.15rem; } .badge-warn { background: rgba(245,158,11,0.15); color: var(--warn-color); } .badge-ok { background: rgba(110,231,183,0.15); color: var(--ok-color); } .badge-info { background: rgba(96,165,250,0.15); color: var(--info-color); } - - .card-title { font-size: 0.9rem; font-weight: 600; color: var(--text); line-height: 1.4; } - .card-body { font-size: 0.84rem; color: var(--text2); line-height: 1.65; } - + .card-title { font-size: 0.88rem; font-weight: 600; color: var(--text); line-height: 1.4; } + .card-body { font-size: 0.82rem; color: var(--text2); line-height: 1.65; } .action-box { - margin-top: 0.85rem; - background: var(--surface2); - border-radius: var(--radius-sm); - padding: 0.75rem 1rem; - font-size: 0.82rem; - color: var(--text2); + margin-top: 0.75rem; background: var(--surface2); + border-radius: var(--radius-sm); padding: 0.65rem 0.85rem; + font-size: 0.8rem; color: var(--text2); } .action-label { - font-size: 0.65rem; - font-weight: 700; - letter-spacing: 0.1em; - color: var(--accent); - text-transform: uppercase; - display: block; - margin-bottom: 0.3rem; + font-size: 0.63rem; font-weight: 700; letter-spacing: 0.1em; + color: var(--accent); text-transform: uppercase; display: block; margin-bottom: 0.25rem; } - /* Listener table */ - .table-wrap { overflow-x: auto; margin-top: 0.75rem; } - table { width: 100%; border-collapse: collapse; font-size: 0.82rem; } + /* Port table */ + .table-wrap { overflow-x: auto; } + table { width: 100%; border-collapse: collapse; font-size: 0.8rem; } thead th { - text-align: left; - padding: 0.5rem 0.75rem; - font-size: 0.68rem; - font-weight: 700; - letter-spacing: 0.08em; - text-transform: uppercase; - color: var(--text3); - border-bottom: 1px solid var(--border); + text-align: left; padding: 0.45rem 0.6rem; + font-size: 0.65rem; font-weight: 700; letter-spacing: 0.08em; + text-transform: uppercase; color: var(--text3); border-bottom: 1px solid var(--border); } tbody tr { border-bottom: 1px solid var(--border); } tbody tr:last-child { border-bottom: none; } - tbody td { padding: 0.45rem 0.75rem; color: var(--text2); vertical-align: top; } - .addr-cell { font-family: monospace; font-size: 0.78rem; color: var(--text3); } + tbody td { padding: 0.4rem 0.6rem; color: var(--text2); vertical-align: top; } + .addr-cell { font-family: monospace; font-size: 0.75rem; color: var(--text3); } .port-cell { font-family: monospace; font-weight: 600; color: var(--text); } - .scope-all { color: var(--warn-color); font-size: 0.8rem; } - .scope-localhost { color: var(--ok-color); font-size: 0.8rem; } - .scope-specific { color: var(--info-color); font-size: 0.8rem; } - .hint-line { font-size: 0.73rem; color: var(--text3); margin-top: 0.1rem; } + .scope-all { color: var(--warn-color); font-size: 0.78rem; } + .scope-localhost { color: var(--ok-color); font-size: 0.78rem; } + .scope-specific { color: var(--info-color); font-size: 0.78rem; } + .hint-line { font-size: 0.7rem; color: var(--text3); margin-top: 0.1rem; } /* Raw */ details { - background: var(--surface); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 1rem 1.25rem; - margin-top: 2rem; + background: var(--surface); border: 1px solid var(--border); + border-radius: var(--radius); padding: 0.9rem 1.1rem; margin-top: 1.5rem; } - summary { cursor: pointer; font-size: 0.8rem; font-weight: 600; color: var(--text3); user-select: none; } - pre { margin-top: 0.75rem; font-size: 0.72rem; color: var(--text3); overflow: auto; max-height: 20rem; line-height: 1.5; } + summary { cursor: pointer; font-size: 0.78rem; font-weight: 600; color: var(--text3); user-select: none; } + pre { margin-top: 0.6rem; font-size: 0.7rem; color: var(--text3); overflow: auto; max-height: 18rem; line-height: 1.5; } + .disclaimer { font-size: 0.73rem; color: var(--text3); border-top: 1px solid var(--border); padding-top: 0.9rem; margin-top: 1.5rem; } - .disclaimer { font-size: 0.75rem; color: var(--text3); border-top: 1px solid var(--border); padding-top: 1rem; margin-top: 2rem; } + .col-label { font-size: 0.65rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text3); margin-bottom: 0.75rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--border); } @@ -221,17 +170,33 @@

ブラウザ内のみで動作します。ファイルを選択しても外部にデータは一切送信されません。

+
tools\pc-audit\out\latest.json

+ @@ -246,33 +211,71 @@ } function len(a) { return Array.isArray(a) ? a.length : 0; } + /* ------------------------------------------------------------------ */ + /* Port descriptions */ + /* ------------------------------------------------------------------ */ var PORT_NOTES = { - "135": "Windows 標準(RPC)— 正常", - "139": "Windows ファイル共有(NetBIOS)— LAN 内なら正常", - "445": "Windows ファイル共有(SMB)— LAN では一般的", + "80": "HTTP(Web サーバー)", + "135": "Windows RPC — 正常", + "139": "NetBIOS ファイル共有 — LAN 内なら正常", + "443": "HTTPS(Web サーバー)", + "445": "SMB ファイル共有 — LAN 内なら正常", "843": "Adobe Flash 関連またはローカルアプリ", - "2015": "開発用ローカルサーバー(Caddy 等)", - "5040": "Windows 標準サービス", + "902": "VMware 管理", + "912": "VMware 管理", + "1080": "SOCKSプロキシ", + "2015": "Caddy 開発サーバー", + "3000": "開発用ローカルサーバー(Node 等)— 正常", + "3306": "MySQL", + "3389": "リモートデスクトップ(RDP)", + "5040": "Windows 標準サービス — 正常", "5354": "mDNS(ローカルデバイス検索)— 正常", "5357": "Windows ネットワーク検出 — 正常", + "5432": "PostgreSQL", + "5900": "VNC リモートデスクトップ", + "6379": "Redis", "7680": "Windows Update 配信最適化 — 正常", + "8080": "開発用 HTTP サーバー — 正常", + "8443": "開発用 HTTPS サーバー", "17500": "Dropbox — インストール済みなら正常", "17600": "Dropbox — インストール済みなら正常", - "27015": "Steam ゲームクライアント — 正常" + "27015": "Steam — インストール済みなら正常", + "27017": "MongoDB", + "33060": "MySQL X Protocol" }; - function addressNote(addr) { - if (addr === "::" || addr === "0.0.0.0") return "全インターフェース(Windows 標準)"; - if (addr === "::1" || addr === "127.0.0.1") return "localhost — 自分の PC 内だけ、安全"; - if (/^100\./.test(addr)) return "Tailscale VPN — インストール済みなら正常"; - if (/^192\.168\./.test(addr)) return "自宅 / 社内 LAN — 正常"; - if (/^fd7a:115c:/.test(addr)) return "Tailscale VPN(IPv6)— 正常"; + /* Ephemeral port ranges are dynamically assigned by Windows — always normal */ + function isEphemeral(port) { + var p = parseInt(port, 10); + return p >= 49152 && p <= 65535; + } + + function portNote(port, hint) { + if (PORT_NOTES[String(port)]) return PORT_NOTES[String(port)]; + if (hint) return hint; + if (isEphemeral(port)) return "一時ポート(Windows が一時的に割り当て)— 正常"; return ""; } + /* ------------------------------------------------------------------ */ + /* Address notes */ + /* ------------------------------------------------------------------ */ + function addressNote(addr) { + if (addr === "::" || addr === "0.0.0.0") return "全インターフェース(Windows 標準)"; + if (addr === "::1" || addr === "127.0.0.1") return "localhost — PC 内だけ、安全"; + if (/^100\./.test(addr)) return "Tailscale VPN — 正常"; + if (/^192\.168\./.test(addr)) return "LAN — 正常"; + if (/^fd7a:115c:/.test(addr)) return "Tailscale VPN(IPv6)— 正常"; + if (/^::1$/.test(addr)) return "localhost(IPv6)— 安全"; + return ""; + } + + /* ------------------------------------------------------------------ */ + /* Render: Summary */ + /* ------------------------------------------------------------------ */ function renderSummary(data) { - var uc = data.userContext || {}; - var m = data.machine || {}; + var uc = data.userContext || {}; + var m = data.machine || {}; var uac = data.uac || {}; var net = data.network || {}; var listeners = Array.isArray(net.TcpListeners) ? net.TcpListeners.length : 0; @@ -292,7 +295,7 @@ ]; if (shareSafe) items.unshift({ label: "モード", value: "ShareSafe" }); - var html = '
概要
'; + var html = '
'; items.forEach(function (it) { html += '
' + esc(it.label) + '
' + esc(it.value) + '
'; }); @@ -300,35 +303,41 @@ document.getElementById("summary").innerHTML = html; } + /* ------------------------------------------------------------------ */ + /* Render: Diff */ + /* ------------------------------------------------------------------ */ function renderDiff(data) { var df = data.diffFromPrevious; if (!df) return; - var html = '
前回との差分
'; + var html = '
'; if (df.HasPrevious === false) { - html += '
前回データなし

' + esc(df.NoteJa || "初回実行のため差分はありません。") + '

'; + html += '
前回データなし

' + esc(df.NoteJa || "初回実行のため差分はありません。") + '

'; } else { - html += '
前回(' + esc(df.PreviousGeneratedAtUtc || "") + ')との比較
'; + html += '
前回との差分
'; var tcp = df.TcpListeners || {}; var envd = df.EnvLikeRelPaths || {}; var hkcu = df.HKCU_Run || {}; var hklm = df.HKLM_Run || {}; - html += '
'; + html += '
'; [ { label: "TCP 追加/削除", value: (tcp.AddedCount || 0) + " / " + (tcp.RemovedCount || 0) }, { label: ".env 追加/削除", value: (envd.AddedCount || 0) + " / " + (envd.RemovedCount || 0) }, - { label: "Run HKCU 追加/削除", value: (hkcu.AddedCount || 0) + "/" + (hkcu.RemovedCount || 0) }, - { label: "Run HKLM 追加/削除", value: (hklm.AddedCount || 0) + "/" + (hklm.RemovedCount || 0) } + { label: "Run HKCU +/-", value: (hkcu.AddedCount || 0) + "/" + (hkcu.RemovedCount || 0) }, + { label: "Run HKLM +/-", value: (hklm.AddedCount || 0) + "/" + (hklm.RemovedCount || 0) } ].forEach(function (r) { html += '
' + esc(r.label) + '
' + esc(r.value) + '
'; }); html += '
'; - if (df.UserIsAdminChanged) html += '

管理者権限の状態が前回と変わりました。

'; - if (df.UacEnableLuaChanged) html += '

UAC 設定が前回と変わりました。

'; + if (df.UserIsAdminChanged) html += '

管理者権限の状態が前回と変わりました。

'; + if (df.UacEnableLuaChanged) html += '

UAC 設定が前回と変わりました。

'; } html += '
'; document.getElementById("diff").innerHTML = html; } + /* ------------------------------------------------------------------ */ + /* Render: Guidance cards */ + /* ------------------------------------------------------------------ */ function buildGuidanceItems(data) { var list = Array.isArray(data.guidanceJa) ? data.guidanceJa : []; if (list.length) return list; @@ -344,24 +353,24 @@ out.push({ Level:"ok", Title:"管理者権限は検出されませんでした", Body:"この監査実行時点では管理者権限は確認されていません。" }); } if (uac.EnableLUA === 0) { - out.push({ Level:"warn", Title:"UAC(確認ダイアログ)が無効になっています", Body:"アプリが PC 設定を変更しようとしても確認ダイアログが出ない状態です。マルウェアが気づかず実行されやすくなります。", ActionJa:"社内 IT に「UAC を有効にしてほしい」と伝える。設定変更は IT に依頼するのが安全です。" }); + out.push({ Level:"warn", Title:"UAC(確認ダイアログ)が無効です", Body:"アプリが PC 設定を変更しようとしても確認ダイアログが出ない状態です。マルウェアが気づかず実行されやすくなります。", ActionJa:"社内 IT に「UAC を有効にしてほしい」と伝える。" }); } if (ssh.DirectoryExists && len(ssh.Files) > 0) { - out.push({ Level:"info", Title:"SSH 鍵ファイルがあります", Body:"サーバーへの接続に使う鍵ファイルが ~/.ssh フォルダに存在します。中身はこのツールでは読んでいません。鍵が漏えいするとサーバーに不正アクセスされる可能性があります。", ActionJa:"鍵ファイルのバックアップがあるか確認する。パスフレーズ(パスワード)を設定済みか、エンジニアに確認する。" }); + out.push({ Level:"info", Title:"SSH 鍵ファイルがあります", Body:"サーバーへの接続に使う鍵ファイルが ~/.ssh フォルダに存在します。鍵が漏えいするとサーバーに不正アクセスされる可能性があります。", ActionJa:"鍵ファイルのバックアップがあるか確認する。パスフレーズ(パスワード)を設定済みか、エンジニアに確認する。" }); } if (envs > 0) { - out.push({ Level:"info", Title:".env ファイルが " + envs + " 件見つかりました", Body:"API キーやパスワードが書かれることが多いファイル名です。中身はこのツールでは読んでいません。これが Git(ソースコード管理)に含まれてしまうと、キーが外部に漏えいします。", ActionJa:"エンジニアに「.env が Git に含まれていないか確認して」と伝える。" }); + out.push({ Level:"info", Title:".env ファイルが " + envs + " 件見つかりました", Body:"API キーやパスワードが書かれることが多いファイル名です。中身はこのツールでは読んでいません。Git に含まれると外部に漏えいします。", ActionJa:"エンジニアに「.env が Git に含まれていないか確認して」と伝える。" }); } var lc = Array.isArray(net.TcpListeners) ? net.TcpListeners.length : 0; if (lc > 45) { - out.push({ Level:"info", Title:"待ち受けポートが多めです(" + lc + " 件)", Body:"PC 上で多くのサービスが通信を受け付けています。使っていないアプリが起動したままになっている可能性があります。", ActionJa:"エンジニアに一覧を見てもらい、不要なアプリがないか確認してもらう。" }); + out.push({ Level:"info", Title:"待ち受けポートが多めです(" + lc + " 件)", Body:"多くのサービスが通信を受け付けています。使っていないアプリが起動したままの可能性があります。", ActionJa:"エンジニアに一覧を見てもらい、不要なアプリがないか確認してもらう。" }); } return out; } function renderGuidance(data) { var items = buildGuidanceItems(data); - var html = '
チェック項目
'; + var html = ""; items.forEach(function (item) { var level = (item.Level || "info").toLowerCase(); var badgeClass = level === "warn" ? "badge-warn" : (level === "ok" ? "badge-ok" : "badge-info"); @@ -378,6 +387,9 @@ document.getElementById("guidance").innerHTML = html; } + /* ------------------------------------------------------------------ */ + /* Render: Port list */ + /* ------------------------------------------------------------------ */ function renderListeners(data) { var net = data.network || {}; var li = net.ListenerInsights; @@ -385,42 +397,46 @@ if (!rows.length) return; var s = (li && li.Summary) || {}; - var html = '
待ち受けポート一覧
'; - html += '

'; - html += '「待ち受けポート」は PC 上で外部からの通信を受け付けているサービスの一覧です。
'; - html += 'localhost のものは自分の PC 内だけで通信するため安全です。'; - html += '::(コロン2つ) / 0.0.0.0 は Windows 標準で出ることが多く、通常は問題ありません。'; + var html = ''; + html += '

'; + html += 'localhost は自分の PC 内だけ、安全。'; + html += '
:: / 0.0.0.0 は Windows 標準で出ることが多く、通常は問題なし。'; + html += '
日本語説明がないポートは 一時ポート(Windows が自動割当)で、接続ごとに番号が変わる正常な動作です。'; html += '

'; + if (s.AllInterfacesCount != null) { html += '
'; - html += '全インターフェース: ' + s.AllInterfacesCount + ''; - html += 'localhost のみ: ' + s.LocalhostOnlyCount + ''; - html += '合計: ' + s.TotalListenRows + ''; + html += '全IF: ' + s.AllInterfacesCount + ''; + html += 'localhost: ' + s.LocalhostOnlyCount + ''; + html += '合計: ' + s.TotalListenRows + ''; html += '
'; } + html += '
'; - html += ''; - var shown = Math.min(rows.length, 50); + html += ''; + var shown = Math.min(rows.length, 60); for (var i = 0; i < shown; i++) { var r = rows[i]; - var scopeClass = r.BindScope === "allInterfaces" ? "scope-all" : (r.BindScope === "localhost" ? "scope-localhost" : "scope-specific"); - var addrNote = addressNote(r.Address || ""); - var portNote = PORT_NOTES[String(r.Port)] || (r.WellKnownHint || ""); + var sc = r.BindScope === "allInterfaces" ? "scope-all" : (r.BindScope === "localhost" ? "scope-localhost" : "scope-specific"); + var aN = addressNote(r.Address || ""); + var pN = portNote(r.Port, r.WellKnownHint); html += ''; - html += ''; + html += ''; html += ''; - html += ''; - html += ''; + html += ''; + html += ''; html += ''; } html += '
AddressPort種類用途の目安
AddressPort種類用途
' + esc(r.Address || "") + (addrNote ? '
' + esc(addrNote) + '
' : '') + '
' + esc(r.Address || "") + (aN ? '
' + esc(aN) + '
' : '') + '
' + esc(r.Port || "") + '' + esc(r.BindScope || "") + '' + esc(portNote) + '' + esc(r.BindScope || "") + '' + esc(pN) + '
'; - if (rows.length > 50) { - html += '

先頭 50 行のみ表示。全データは下の生データを参照。

'; + if (rows.length > 60) { + html += '

先頭 60 行のみ表示。全データは下の生データを参照。

'; } - html += '
'; document.getElementById("listeners").innerHTML = html; } + /* ------------------------------------------------------------------ */ + /* File load */ + /* ------------------------------------------------------------------ */ document.getElementById("f").addEventListener("change", function (ev) { var file = ev.target.files && ev.target.files[0]; if (!file) return;