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 @@ブラウザ内のみで動作します。ファイルを選択しても外部にデータは一切送信されません。
+' + esc(df.NoteJa || "初回実行のため差分はありません。") + '
'; + html += '' + esc(df.NoteJa || "初回実行のため差分はありません。") + '
'; } else { - html += '管理者権限の状態が前回と変わりました。
'; - if (df.UacEnableLuaChanged) html += 'UAC 設定が前回と変わりました。
'; + if (df.UserIsAdminChanged) html += '管理者権限の状態が前回と変わりました。
'; + if (df.UacEnableLuaChanged) html += 'UAC 設定が前回と変わりました。
'; } 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 += '
| Address | Port | 種類 | 用途の目安 | |||
|---|---|---|---|---|---|---|
| Address | Port | 種類 | 用途 | |||
| ' + esc(r.Address || "") + (addrNote ? ' ' + esc(addrNote) + ' ' : '') + ' | ';
+ html += '' + esc(r.Address || "") + (aN ? ' ' + esc(aN) + ' ' : '') + ' | ';
html += '' + esc(r.Port || "") + ' | '; - html += '' + esc(r.BindScope || "") + ' | '; - html += '' + esc(portNote) + ' | '; + html += '' + esc(r.BindScope || "") + ' | '; + html += '' + esc(pN) + ' | '; html += '
先頭 50 行のみ表示。全データは下の生データを参照。
'; + if (rows.length > 60) { + html += '先頭 60 行のみ表示。全データは下の生データを参照。
'; } - html += '