+14
-13
@@ -94,3 +94,3 @@ "use strict"; | ||
| const execPath = getExecPath(filePath, config.extensions); | ||
| let env; | ||
| let env, cgiProcess, ac, timeoutId; | ||
@@ -114,2 +114,13 @@ try { | ||
| }); | ||
| const result = spawnProcess({ | ||
| execPath, | ||
| fullFilePath, | ||
| env, | ||
| config, | ||
| req, | ||
| res, | ||
| useShell: execPath ? !absolutePaths[execPath] : false, | ||
| }); | ||
| ({ cgiProcess, ac, timeoutId } = result); | ||
| } catch (err) { | ||
@@ -120,3 +131,3 @@ if (err.code === "ENOENT") { | ||
| } | ||
| if (err.code === "EACCES") { | ||
| if (err.code === "EACCES" || err.code === "EFTYPE") { | ||
| const message = `${fullFilePath} is not executable.`; | ||
@@ -130,12 +141,2 @@ errorHandler.apply({ req, res, config }, [message]); | ||
| const { cgiProcess, ac, timeoutId } = spawnProcess({ | ||
| execPath, | ||
| fullFilePath, | ||
| env, | ||
| config, | ||
| req, | ||
| res, | ||
| useShell: execPath ? !absolutePaths[execPath] : false, | ||
| }); | ||
| await streamRequestPayload(cgiProcess, req, res, config); | ||
@@ -145,3 +146,3 @@ streamResponsePayload(cgiProcess, req, res, config); | ||
| res.on("close", () => { | ||
| ac.abort(); | ||
| ac?.abort(); | ||
| req.destroy(); | ||
@@ -148,0 +149,0 @@ clearTimeout(timeoutId); |
+96
-33
@@ -142,54 +142,117 @@ # Sample CGI/Perl Scripts | ||
| use warnings; | ||
| use CGI qw(:standard); | ||
| use CGI qw(escapeHTML); | ||
| use File::Spec; | ||
| use File::Basename; | ||
| use URI::Escape; | ||
| use File::Basename qw(dirname); | ||
| use URI::Escape qw(uri_escape); | ||
| # Use SCRIPT_FILENAME environment variable to get script directory | ||
| # 1. Graceful Error Handling Function | ||
| sub show_error { | ||
| my ($message, $status) = @_; | ||
| $status ||= '500 Internal Server Error'; | ||
| print "Status: $status\r\n"; | ||
| print "Content-Type: text/html; charset=UTF-8\r\n\r\n"; | ||
| print <<"HTML"; | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <title>Error</title> | ||
| </head> | ||
| <body> | ||
| <h1>Error</h1> | ||
| <p>@{[ escapeHTML($message) ]}</p> | ||
| </body> | ||
| </html> | ||
| HTML | ||
| exit; | ||
| } | ||
| # 2. Get and validate script path | ||
| my $script_path = $ENV{'SCRIPT_FILENAME'} | ||
| or die "SCRIPT_FILENAME environment variable is not set."; | ||
| or show_error("SCRIPT_FILENAME environment variable is not set.", '500 Server Misconfiguration'); | ||
| my $script_dir = dirname($script_path); | ||
| my $web_base_url = "/cgi-bin"; | ||
| # Get the current script's URL path | ||
| my $script_url_path = $ENV{'SCRIPT_NAME'}; | ||
| $script_url_path =~ s/[^\/]+$//; # Remove script filename, keep path prefix | ||
| # prepend "/cgi-bin/" to the path | ||
| $script_url_path = $web_base_url . $script_url_path; | ||
| # 3. Cleanly resolve script URL prefix without duplicate slashes | ||
| my $web_base_url = "/cgi-bin"; | ||
| my $raw_script_name = $ENV{'SCRIPT_NAME'} // ''; | ||
| (my $script_url_path = $raw_script_name) =~ s{[^/]+$}{}; # Remove script file name | ||
| print header(-type => 'text/html; charset=UTF-8'); | ||
| print start_html("Directory Listing"); | ||
| print h1("Files and Folders in $script_dir"); | ||
| # Combine base paths cleanly | ||
| my $base_href = File::Spec::Unix->catdir($web_base_url, $script_url_path); | ||
| $base_href =~ s{/{2,}}{/}g; | ||
| $base_href .= '/' unless $base_href =~ m{/$}; | ||
| opendir(my $dh, $script_dir) or die "Cannot open directory $script_dir: $!"; | ||
| my @items = grep { $_ ne '.' && $_ ne '..' } readdir($dh); | ||
| # 4. Open and read directory safely | ||
| opendir(my $dh, $script_dir) | ||
| or show_error("Cannot read directory: $!", '500 Internal Server Error'); | ||
| my @raw_items = grep { $_ ne '.' && $_ ne '..' } readdir($dh); | ||
| closedir($dh); | ||
| # Case-insensitive natural sort (directories first, then files) | ||
| my @items = sort { | ||
| my $path_a = File::Spec->catfile($script_dir, $a); | ||
| my $path_b = File::Spec->catfile($script_dir, $b); | ||
| (-d $path_b <=> -d $path_a) || (lc($a) cmp lc($b)) | ||
| } @raw_items; | ||
| # 5. Output HTTP Header & Page Content | ||
| print "Content-Type: text/html; charset=UTF-8\r\n\r\n"; | ||
| print <<"HTML"; | ||
| <!DOCTYPE html> | ||
| <html lang="en"> | ||
| <head> | ||
| <meta charset="UTF-8"> | ||
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||
| <title>Directory Listing</title> | ||
| <style> | ||
| body { font-family: system-ui, -apple-system, sans-serif; margin: 2rem; background: #fafafa; color: #333; } | ||
| h1 { font-size: 1.5rem; word-break: break-all; } | ||
| ul { list-style-type: none; padding-left: 0; } | ||
| li { margin: 0.4rem 0; } | ||
| a { text-decoration: none; color: #0066cc; } | ||
| a:hover { text-decoration: underline; } | ||
| .dir { font-weight: bold; color: #1a0dab; } | ||
| .tag { font-size: 0.8em; color: #666; font-weight: normal; margin-left: 0.3rem; } | ||
| </style> | ||
| </head> | ||
| <body> | ||
| <h1>Files and Folders in @{[ escapeHTML($script_dir) ]}</h1> | ||
| HTML | ||
| if (@items) { | ||
| print ul( | ||
| map { | ||
| my $full_path = File::Spec->catfile($script_dir, $_); | ||
| my $encoded = uri_escape($_); | ||
| my $label = $_; | ||
| my $is_dir = -d $full_path; | ||
| print " <ul>\n"; | ||
| for my $item (@items) { | ||
| my $full_path = File::Spec->catfile($script_dir, $item); | ||
| my $is_dir = -d $full_path; | ||
| # Preserve slashes in URI encoding if item contains them | ||
| my $encoded = uri_escape($item); | ||
| my $label = escapeHTML($item); | ||
| my $href = $base_href . $encoded . ($is_dir ? '/' : ''); | ||
| # Create the correct URL pointing to the file or folder | ||
| my $href = $script_url_path . $encoded; | ||
| $href .= '/' if $is_dir; | ||
| my $dir_class = $is_dir ? ' class="dir"' : ''; | ||
| my $dir_tag = $is_dir ? ' <span class="tag">[DIR]</span>' : ''; | ||
| my $target = $is_dir ? '' : ' target="_blank" rel="noopener"'; | ||
| li( | ||
| a({ href => $href, target => '_blank' }, $label) | ||
| . ($is_dir ? " [DIR]" : "") | ||
| ); | ||
| } sort @items | ||
| ); | ||
| print qq{ <li><a href="$href"$dir_class$target>$label</a>$dir_tag</li>\n}; | ||
| } | ||
| print " </ul>\n"; | ||
| } else { | ||
| print p("No files or directories found."); | ||
| print " <p>No files or directories found.</p>\n"; | ||
| } | ||
| print end_html; | ||
| print <<"HTML"; | ||
| </body> | ||
| </html> | ||
| HTML | ||
| 1; | ||
| ``` | ||
| Feel free to modify and test these scripts in your CGI environment! |
+1
-1
| { | ||
| "name": "cgi-core", | ||
| "description": "Lightweight, zero-dependency middleware for hosting CGI scripts with HTTP/1.1 support", | ||
| "version": "1.4.3", | ||
| "version": "1.4.4", | ||
| "homepage": "https://lfortin.github.io/cgi-core", | ||
@@ -6,0 +6,0 @@ "repository": { |
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
69374
2.92%1395
0.07%