Skip to content

Commit

Permalink
Merge pull request #139 from ilovenoah/136-add-return-403-forbidden-i…
Browse files Browse the repository at this point in the history
…f-cgi-script-is-not-allowed-to-be-executed

136 add return 403 forbidden if cgi script is not allowed to be executed
  • Loading branch information
ilovenoah authored Mar 2, 2024
2 parents 645a9c4 + 95d9041 commit 1f84d49
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 12 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/integration_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
- name: Change privileges of CGI scripts
run: chmod +x ./tests/simple_server_function_test/www/cgi-bin/*

- name: Change privileges of CGI scripts for a specific test
run: chmod -x ./tests/simple_server_function_test/www/cgi-bin/unauthorized_cgi_script.py

- name: Create an empty directory for test
run: mkdir ./tests/simple_server_function_test/upload ./tests/simple_server_function_test/www/delete

Expand Down
4 changes: 3 additions & 1 deletion inc/Response.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
#include "utils.hpp"

class Response {
public:
enum accessResult { EXECUTABLE, UNAUTHORIZED, NOTMATCHED };
private:
CGIHandler _cgiHandler;
std::string _httpVersion;
Expand Down Expand Up @@ -54,7 +56,7 @@ class Response {
bool _isLocalRedirectResponse();
bool _isValidCGIResponse() const;
bool _shouldAutoIndexed() const;
bool _shouldExecCGIScript();
Response::accessResult _shouldExecCGIScript();
ClientSocket::csphase _setEntireDataWithBody(std::string const &status,
std::string const &body,
bool shouldKeepAlive);
Expand Down
45 changes: 34 additions & 11 deletions src/response/Response.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,11 @@ void Response::_setErrorResponse(const std::string &status,
this->_location->getErrorPages().find(status);
if (iter != this->_location->getErrorPages().end()) {
errorPagePath = iter->second;
} else {
this->_setEntireDataWithBody(
status, this->_statusMap.find(status)->second.second,
shouldKeepAlive);
return;
}
} else {
std::map<std::string, std::string>::const_iterator iter =
Expand Down Expand Up @@ -617,7 +622,7 @@ bool Response::_shouldAutoIndexed() const {
return false;
}

bool Response::_shouldExecCGIScript() {
Response::accessResult Response::_shouldExecCGIScript() {
std::string scriptPath(this->_actPath);
std::map<std::string, std::string> cgiExtenstions;
if (this->_location != NULL) {
Expand All @@ -631,23 +636,37 @@ bool Response::_shouldExecCGIScript() {
iter != cgiExtenstions.end(); ++iter) {
std::size_t posExtension(scriptPath.find_last_of('.'));
if (posExtension == std::string::npos) {
return false;
return Response::NOTMATCHED;
}
if (scriptPath.substr(posExtension).compare(iter->first) == 0 &&
utils::isAccess(scriptPath, X_OK) == true &&
utils::isAccess(iter->second, X_OK)) {
this->_cgiHandler.setRuntimePath(iter->second);
this->_cgiHandler.setScriptPath(scriptPath);
return true;
if (scriptPath.substr(posExtension).compare(iter->first) == 0) {
bool isRuntimeExecutable = utils::isAccess(iter->second, X_OK);
bool isRuntimeExisted = utils::isAccess(iter->second, F_OK);

if (isRuntimeExecutable == false && isRuntimeExisted == true) {
return Response::UNAUTHORIZED;
} else if (isRuntimeExecutable == false && isRuntimeExisted == false) {
return Response::NOTMATCHED;
}

bool isExecutable = utils::isAccess(scriptPath, X_OK);
bool isExisted = utils::isAccess(scriptPath, F_OK);

if (isExecutable == false && isExisted == true) {
return Response::UNAUTHORIZED;
} else if (isExecutable == true && isExisted == true) {
this->_cgiHandler.setRuntimePath(iter->second);
this->_cgiHandler.setScriptPath(scriptPath);
return Response::EXECUTABLE;
}
}
}
std::size_t posSlash(scriptPath.find_last_of('/'));
if (posSlash == std::string::npos) {
return false;
return Response::NOTMATCHED;
}
scriptPath.erase(posSlash);
}
return false;
return Response::NOTMATCHED;
}

ClientSocket::csphase Response::load(Config &config, Request &request) {
Expand All @@ -673,7 +692,8 @@ ClientSocket::csphase Response::load(Config &config, Request &request) {
if (this->_shouldRedirect() == true) {
return this->_setRedirectResponse(request, request.shouldKeepAlive());
}
if (this->_shouldExecCGIScript() == true) {
Response::accessResult accessResultCGI = this->_shouldExecCGIScript();
if (accessResultCGI == Response::EXECUTABLE) {
if (this->_cgiHandler.init(request, *(this->_server), this->_actPath) ==
false) {
this->_setErrorResponse("500", false);
Expand All @@ -684,6 +704,9 @@ ClientSocket::csphase Response::load(Config &config, Request &request) {
return ClientSocket::SEND;
}
return ClientSocket::RECV;
} else if (accessResultCGI == Response::UNAUTHORIZED) {
this->_setErrorResponse("403", false);
return ClientSocket::SEND;
}
if (request.getMethod() == "GET") {
return this->_setGetResponse(request);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
host, localhost
port, 8080


GET /cgi-bin/unauthorized_cgi_script.py HTTP/1.1
Host: test
Connection: close
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
HTTP/1.1 403 Forbidden
Connection: close
Content-Length: 9

Forbidden
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import os

# ステータスとコンテントタイプのヘッダーを出力
print("Status: 200 OK")
print("Content-Type: text/html")
print() # ヘッダーの終わりを示す空行

print("<!DOCTYPE html>")
print("<html>")
print("<head>")
print("<title>CGI Environment Variables</title>")
print("</head>")
print("<body>")
print("<h1>Environment Variables</h1>")
print("<table border='1'>")
print("<tr><th>Variable</th><th>Value</th></tr>")

# 環境変数を取得してテーブルに表示
# for key, value in os.environ.items():
# print(f"<tr><td>{key}</td><td>{value}</td></tr>")

print("</table>")
print("</body>")
print("</html>")

0 comments on commit 1f84d49

Please sign in to comment.