Skip to content

Commit

Permalink
improve oobtkube and output results in SARIF format (RedHatProductSec…
Browse files Browse the repository at this point in the history
  • Loading branch information
jeremychoi authored Feb 13, 2024
1 parent 9a0def3 commit 84f039c
Showing 1 changed file with 137 additions and 24 deletions.
161 changes: 137 additions & 24 deletions scanners/generic/tools/oobtkube.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,12 @@
#
######################################
import argparse
import json
import os
import queue
import re
import socket
import subprocess
import sys
import threading
import time
Expand Down Expand Up @@ -60,53 +64,127 @@ def scan_with_k8s_config(cfg_file_path, ipaddr, port):
print(f"Command run: {cmd}")
os.system(cmd)

kube_cmd = f"kubectl apply -f {tmp_file}"
# if using 'apply' and a resource already exists, the command won't run as it returns as 'unchanged'
# therefore 'create' and 'replace' are used
kube_cmd = f"kubectl create -f {tmp_file} > /dev/null 2>&1; kubectl replace -f {tmp_file}"

print(f"Command run: {kube_cmd}")
os.system(kube_cmd)


def start_socket_listener(port, data_received, stop_event, duration):
def start_socket_listener(port, shared_queue, data_received, stop_event, duration):
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind((SERVER_HOST, port))
try:
server_socket.bind((SERVER_HOST, port))
except OSError as e:
print(
f"{e}. Stopping the server. It might take a few seconds. Please try again later."
)
stop_event.set()
server_socket.close()
return
server_socket.settimeout(duration)
server_socket.listen(1)

print(f"Listening on port {port}")

try:
client_socket = None
client_socket, client_address = server_socket.accept()
print(f"Accepted connection from {client_address}")

while not stop_event.is_set():
try:
data = client_socket.recv(1024)
if not data:
break
data = client_socket.recv(1024)
if not data:
break

print("Received data:", data.decode("utf-8"))
shared_queue.put(data.decode("utf-8"))

# Send a custom response back to the client
response = "HTTP/1.1 200 OK\r\n\r\nfrom oob_listener!\n"
client_socket.send(response.encode("utf-8"))
# Send a custom response back to the client
response = "HTTP/1.1 200 OK\r\n\r\nfrom oob_listener!\n"
client_socket.send(response.encode("utf-8"))

data_received.set()
data_received.set()

# Stop the listener after the first request
stop_event.set()
break
# Stop the listener after the first request
stop_event.set()
break

except socket.timeout:
pass
except TimeoutError:
print("Timeout reached. Stopping the server.")
pass

except Exception as e:
raise RuntimeError("An error occurred. See logs for details.") from e

finally:
client_socket.close()
server_socket.close()


if client_socket:
client_socket.close()
if server_socket:
server_socket.close()


def convert_to_sarif_json(result_message, tool_name, artifact_url="", snippet=""):
sarif_output = {
"$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json",
"version": "2.1.0",
"runs": [
{
"tool": {"driver": {"name": tool_name, "version": "1.0.0"}},
"results": [
{
"level": "error",
"locations": [
{
"physicalLocation": {
"artifactLocation": {"uri": artifact_url},
"region": {
"startLine": 1,
"properties": {
# pylint: disable=C0301
"startLineFailure": "Resolved invalid start line: 0 - used fallback value instead."
},
"snippet": {"text": snippet},
},
}
}
],
"message": {"text": result_message},
"ruleId": "RAPIDAST-OOBTKUBE-00001",
}
],
}
],
}
return json.dumps(sarif_output)


def get_kubernetes_api_url():
try:
# Run kubectl cluster-info command and capture the output
output = subprocess.check_output(["kubectl", "cluster-info"]).decode("utf-8")

# Find the line containing the Kubernetes master URL
lines = output.split("\n")
for line in lines:
if "is running at" in line:
# Use regular expression to extract the URL
url_match = re.search(r"(https?://[^\s;]+:[0-9]+)", line)

if url_match:
# Extract and print the URL
api_url = url_match.group(1)
print("Kubernetes API server URL:", api_url)
return api_url
# Return None if URL is not found
return None
except subprocess.CalledProcessError as e:
# Handle error if kubectl command fails
print("Error:", e)
return None


# pylint: disable=R0915
def main():
# Parse command-line arguments
parser = argparse.ArgumentParser(
Expand Down Expand Up @@ -136,6 +214,13 @@ def main():
parser.add_argument(
"-f", "--filename", type=str, required=True, help="Kubernetes config file path"
)
# add argument for '-o' to output the result to a file
parser.add_argument(
"-o",
"--output",
type=str,
help="Output result to a file in the SARIF format (default: stdout)",
)

args = parser.parse_args()

Expand All @@ -150,18 +235,23 @@ def main():
# Create a few threading events
data_received = threading.Event()
stop_event = threading.Event()
shared_queue = queue.Queue()

# Start socket listener in a separate thread
socket_listener_thread = threading.Thread(
target=start_socket_listener,
args=(args.port, data_received, stop_event, args.duration),
args=(args.port, shared_queue, data_received, stop_event, args.duration),
)
socket_listener_thread.start()

# Wait for a while to ensure the socket listener is up
# You may need to adjust this delay based on your system
time.sleep(5)

if stop_event.is_set():
print("Socket listener failed to start. Exiting...")
sys.exit(1)

print("Listener thread started")

# Record the start time for the main function
Expand All @@ -182,9 +272,32 @@ def main():
socket_listener_thread.join()

if data_received.is_set():
print(f"RESULT: {MESSAGE_DETECTED}")
result_message = f"{MESSAGE_DETECTED}"
tool = "RapiDAST-oobtkube"

kubernetes_api_url = get_kubernetes_api_url()
if kubernetes_api_url:
print("Kubernetes API URL:", kubernetes_api_url)
artifact_url = kubernetes_api_url
else:
print("Failed to retrieve Kubernetes API URL.")
artifact_url = "a target k8s operator"

snippet = shared_queue.get()
print("Request received:", snippet)

sarif_output = convert_to_sarif_json(
result_message, tool, artifact_url, snippet
)

if args.output:
with open(args.output, "w", encoding="utf-8") as f:
f.write(sarif_output)
else:
print(f"OOBTKUBE RESULT: {MESSAGE_DETECTED}")
print(sarif_output)
else:
print(f"RESULT: {MESSAGE_NOT_DETECTED}")
print(f"OOBTKUBE RESULT: {MESSAGE_NOT_DETECTED}")
sys.exit(0)


Expand Down

0 comments on commit 84f039c

Please sign in to comment.