📄 v.php

PHP 131 行 · 4,980 bytes ← 返回首页
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 129 130 131
<?php
/**
 * CmdCode Source Viewer — 纯源代码阅读器
 * 
 * 用法:v.php?f=proxy.php
 * 安全:只允许读取 /www/source/ 目录下的预定义白名单文件
 * 不会执行 PHP/HTML,直接显示原始源代码
 */

// ── 白名单(只允许列出这些文件) ──
$WHITELIST = [
    'ui.html',
    'proxy.php',
    'config.enc.php',
    'htaccess-example',
    'long-task-cron-worker.sh',
    'cron.d-long-task-worker',
    'long-task-worker-check.sh',
    'v.php',
];

$file = $_GET['f'] ?? '';
$file = basename($file); // 防路径穿越(第一层防御)

if (!in_array($file, $WHITELIST)) {
    header('Content-Type: text/html; charset=utf-8');
    echo '<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><title>文件不存在</title>';
    echo '<style>body{font-family:sans-serif;background:#0d1117;color:#c9d1d9;padding:40px;text-align:center}h1{color:#ff6b6b}a{color:#58a6ff}</style>';
    echo '</head><body><h1>❌ 文件不存在或不在白名单中</h1>';
    echo '<p>允许的文件: ' . implode(', ', $WHITELIST) . '</p>';
    echo '<p><a href="index.html">← 返回首页</a></p>';
    echo '</body></html>';
    exit;
}

// ── 读取原始源代码(不执行!) ──
// 第二层防御:realpath() 确保解析后的路径在 __DIR__ 内
$path = __DIR__ . '/' . $file;
$realPath = realpath($path);
$baseDir = realpath(__DIR__);
if ($realPath === false || strncmp($realPath, $baseDir, strlen($baseDir)) !== 0) {
    http_response_code(403);
    echo '文件路径无效';
    exit;
}
$source = file_get_contents($realPath);
if ($source === false) {
    http_response_code(500);
    echo '读取文件失败';
    exit;
}

// 获取文件信息
$lines = substr_count($source, "\n") + 1;
$bytes = strlen($source);
$ext = strtolower(pathinfo($file, PATHINFO_EXTENSION));

// 语言标签
$langMap = [
    'html' => 'HTML',
    'php'  => 'PHP',
    'sh'   => 'Bash',
    'conf' => 'Config',
];
$lang = $langMap[$ext] ?? 'Text';

// ── 输出页面 ──
header('Content-Type: text/html; charset=utf-8');
?><!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>源代码: <?= htmlspecialchars($file) ?> — CmdCode 开源</title>
<style>
*{margin:0;padding:0;box-sizing:border-box}
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0d1117;color:#c9d1d9;line-height:1.6}
.header{background:#161b22;border-bottom:1px solid #30363d;padding:14px 20px;display:flex;align-items:center;gap:12px;flex-wrap:wrap}
.header h1{font-size:1.1em;color:#f0f6fc}
.header .meta{color:#8b949e;font-size:0.82em}
.header .lang{display:inline-block;padding:1px 8px;border-radius:6px;font-size:0.75em;font-weight:600}
.lang-html{background:#1f2937;color:#58a6ff;border:1px solid #30363d}
.lang-php{background:#1c2128;color:#7ee787;border:1px solid #30363d}
.lang-bash{background:#1c2128;color:#f5a623;border:1px solid #30363d}
.lang-config{background:#1c2128;color:#ff6b6b;border:1px solid #30363d}
.header a{color:#58a6ff;text-decoration:none;font-size:0.85em}
.header a:hover{text-decoration:underline}
.code-wrap{display:flex;min-height:calc(100vh - 52px)}
.line-nums{padding:14px 0;background:#0d1117;border-right:1px solid #21262d;min-width:52px;text-align:right;user-select:none;flex-shrink:0}
.line-nums span{display:block;padding:0 12px;font-size:0.78em;line-height:1.55;color:#484f58;font-family:"SF Mono","Fira Code",Consolas,monospace}
.code-body{flex:1;overflow-x:auto;padding:14px 0}
.code-body pre{margin:0;padding:0 20px;font-size:0.78em;line-height:1.55;font-family:"SF Mono","Fira Code",Consolas,monospace;white-space:pre;tab-size:4;color:#c9d1d9}
.code-body pre .kw{color:#ff7b72}
.code-body pre .fn{color:#d2a8ff}
.code-body pre .str{color:#a5d6ff}
.code-body pre .cm{color:#8b949e;font-style:italic}
.code-body pre .num{color:#79c0ff}
.code-body pre .tag{color:#7ee787}
.code-body pre .attr{color:#79c0ff}
.code-body pre .val{color:#a5d6ff}
::-webkit-scrollbar{width:6px;height:6px}
::-webkit-scrollbar-track{background:transparent}
::-webkit-scrollbar-thumb{background:#30363d;border-radius:3px}
@media(max-width:600px){
  .header{padding:10px 14px;flex-direction:column;align-items:flex-start;gap:6px}
  .code-wrap{flex-direction:column}
  .line-nums{display:none}
  .code-body pre{padding:0 14px;font-size:0.72em}
}
</style>
</head>
<body>
<div class="header">
  <h1>📄 <?= htmlspecialchars($file) ?></h1>
  <span class="lang lang-<?= $ext ?>"><?= $lang ?></span>
  <span class="meta"><?= $lines ?> 行 · <?= number_format($bytes) ?> bytes</span>
  <a href="index.html">← 返回首页</a>
</div>
<div class="code-wrap">
  <div class="line-nums">
    <?php for ($i = 1; $i <= $lines; $i++): ?>
    <span><?= $i ?></span>
    <?php endfor; ?>
  </div>
  <div class="code-body">
    <pre><code><?= htmlspecialchars($source) ?></code></pre>
  </div>
</div>
</body>
</html>