Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git

Updates security

AuthorDave Jarvis <email>
Date2026-02-05 12:31:17 GMT-0800
Commit5921e935e7a4fec0b5382eed3245cf383ad7fb63
Parent4c66b0f
Git.php
public function __construct( $repoPath ) {
+ $repoPath = realpath($repoPath);
+
+ if ($repoPath === false) {
+ throw new Exception( "Invalid repository path" );
+ }
+
+ $allowedBase = realpath(REPOS_PATH);
+ if ($allowedBase === false || strpos($repoPath, $allowedBase) !== 0) {
+ throw new Exception( "Repository path outside allowed directory" );
+ }
+
$this->repoPath = rtrim( $repoPath, '/' );
$this->gitDir = $this->repoPath;
- if( !is_dir( $this->gitDir . '/objects' ) || !is_dir( $this->gitDir . '/refs' ) ) {
+ if( !is_dir( $this->gitDir . '/objects' ) ||
+ !is_dir( $this->gitDir . '/refs' ) ) {
throw new Exception( "Not a valid bare git repository: {$repoPath}" );
}
*/
private function parseCommitLine( $line ) {
- // Format: Name <email> timestamp timezone
- if( preg_match( '/^( .+? )\s+<( .+? )>\s+( \d+ )\s+( [+-]\d{4} )$/', $line, $matches ) ) {
+ if( preg_match( '/^(.+?)\s+<(.+?)>\s+(\d+)\s+([+-]\d{4})$/', $line, $matches ) ) {
return [
'name' => $matches[1],
}
?>
-
MediaTypeSniffer.php
?>
-
index.php
<?php
require_once __DIR__ . '/config.php';
+require_once __DIR__ . '/security.php';
require_once __DIR__ . '/includes/git_functions.php';
require_once __DIR__ . '/includes/repo_functions.php';
require_once __DIR__ . '/includes/helpers.php';
$action = $_GET['action'] ?? 'list';
-$repo = $_GET['repo'] ?? '';
+$repo = sanitizeRepoName($_GET['repo'] ?? '');
$ref = $_GET['ref'] ?? 'HEAD';
-$path = $_GET['path'] ?? '';
-$hash = $_GET['hash'] ?? '';
+$path = sanitizePath($_GET['path'] ?? '');
+$hash = sanitizeHash($_GET['hash'] ?? '');
+
+$allowed_actions = ['list', 'repo', 'commit', 'blob', 'raw'];
+
+if (!in_array($action, $allowed_actions, true)) {
+ $action = 'list';
+}
+$allowed_themes = ['dark', 'light'];
$current_theme = $_GET['theme'] ?? 'dark';
+
+if (!in_array($current_theme, $allowed_themes, true)) {
+ $current_theme = 'dark';
+}
+
$css_file = ($current_theme === 'dark') ? 'dark.css' : 'light.css';
if ($action === 'raw' && !empty($repo) && !empty($hash)) {
- $name = $_GET['name'] ?? 'file';
- $content = getBlobBinary($repo, $hash);
+ $repoPath = REPOS_PATH . '/' . $repo;
+ if (!is_dir($repoPath)) {
+ http_response_code(404);
+ exit('Repository not found');
+ }
- header('Content-Type: application/octet-stream');
- header('Content-Disposition: attachment; filename="' . $name . '"');
- header('Content-Length: ' . strlen($content));
- echo $content;
+ $name = sanitizeFilename($_GET['name'] ?? 'file');
+
+ try {
+ $content = getBlobBinary($repo, $hash);
+
+ header('Content-Type: application/octet-stream');
+ header('Content-Disposition: attachment; filename="' . $name . '"');
+ header('Content-Length: ' . strlen($content));
+ header('X-Content-Type-Options: nosniff');
+ echo $content;
+ } catch (Exception $e) {
+ http_response_code(404);
+ exit('File not found');
+ }
+
exit;
}
-$page_title = $action === 'list' ? SITE_TITLE : htmlspecialchars($repo) . ' - ' . SITE_TITLE;
+$page_title = $action === 'list'
+ ? SITE_TITLE
+ : htmlspecialchars($repo, ENT_QUOTES, 'UTF-8') . ' - ' . SITE_TITLE;
include __DIR__ . '/views/header.php';
include __DIR__ . '/views/footer.php';
-
security.php
+<?php
+function sanitizeRepoName($repo) {
+ $repo = basename($repo);
+ if (preg_match('/^[a-zA-Z0-9._-]+$/', $repo)) {
+ return $repo;
+ }
+ return '';
+}
+
+function sanitizePath($path) {
+ $path = str_replace(['../', '..\\', "\0"], '', $path);
+ $path = ltrim($path, '/');
+ return $path;
+}
+
+function sanitizeHash($hash) {
+ if (preg_match('/^[a-f0-9]{40}$/i', $hash)) {
+ return $hash;
+ }
+ return '';
+}
+
+function sanitizeFilename($filename) {
+ $filename = basename($filename);
+ $filename = preg_replace('/[^a-zA-Z0-9._-]/', '_', $filename);
+ return $filename;
+}
+
views/index.html
Delta81 lines added, 16 lines removed, 65-line increase