From 51038e8670697f6a76cbe8e3f099209708aed46f Mon Sep 17 00:00:00 2001 From: homelab-runtime-publisher Date: Wed, 22 Jul 2026 21:23:41 +0000 Subject: [PATCH] runtime: publish GITEA-READER-ROUTES-AUDIT-289 --- .../GITEA-READER-ROUTES-AUDIT-289.json | 30 +++++ .../history/GITEA-READER-ROUTES-AUDIT-289.txt | 116 ++++++++++++++++++ runtime/latest.json | 14 +-- runtime/latest.txt | 105 ++++++++++++++-- 4 files changed, 248 insertions(+), 17 deletions(-) create mode 100644 runtime/history/GITEA-READER-ROUTES-AUDIT-289.json create mode 100644 runtime/history/GITEA-READER-ROUTES-AUDIT-289.txt diff --git a/runtime/history/GITEA-READER-ROUTES-AUDIT-289.json b/runtime/history/GITEA-READER-ROUTES-AUDIT-289.json new file mode 100644 index 0000000..ea6f3c8 --- /dev/null +++ b/runtime/history/GITEA-READER-ROUTES-AUDIT-289.json @@ -0,0 +1,30 @@ +{ + "schema_version": 1, + "channel": "homelab-runtime", + "command_id": "GITEA-READER-ROUTES-AUDIT-289", + "status": "OK", + "rc": 0, + "host": "pve01", + "mode": "read-only", + "component": "cluster-audit", + "started_at_utc": "2026-07-22T21:23:40Z", + "finished_at_utc": "2026-07-22T21:23:40Z", + "reference_register_checked": true, + "reference_sha256": "5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e", + "error_register_checked": true, + "error_register_sha256": "08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752", + "command_sha256": "ca1499ccd79985134e654f663bd829da73f07f8aa2af0a227387a97e6c32c66d", + "duplicate_failed_command_blocked": false, + "block_reason": null, + "execution_started": true, + "changes_made": false, + "sanitized": true, + "secrets_included": false, + "private_addresses_included": false, + "raw_evidence_retained_locally": true, + "raw_evidence_sha256": "8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b", + "sanitized_output_sha256": "8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b", + "output_truncated_in_json": false, + "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", + "output": "PREVIOUS_COMMAND_ID=GITEA-READER-BIND-AUDIT-288\nDETAIL= 1\t#!/usr/bin/python3\nDETAIL= 2\timport hashlib\nDETAIL= 3\timport http.server\nDETAIL= 4\timport json\nDETAIL= 5\timport os\nDETAIL= 6\timport pathlib\nDETAIL= 7\timport re\nDETAIL= 8\timport sys\nDETAIL= 9\t\nDETAIL= 10\tROOT=pathlib.Path(\"/var/lib/homelab-reader-v3\")\nDETAIL= 11\tMANIFEST=ROOT/\"manifest.json\"\nDETAIL= 12\tRESULTS=ROOT/\"results\"\nDETAIL= 13\tCAPABILITY=os.environ[\"HOMELAB_READER_CAPABILITY\"]\nDETAIL= 14\tPORT=int(os.environ.get(\"HOMELAB_READER_PORT\",\"18788\"))\nDETAIL= 15\tRESULT_RE=re.compile(r\"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}-[0-9a-f]{64}\\.txt$\")\nDETAIL= 16\t\nDETAIL= 17\tclass Handler(http.server.BaseHTTPRequestHandler):\nDETAIL= 18\t server_version=\"HomelabReader/3\"\nDETAIL= 19\t\nDETAIL= 20\t def log_message(self,fmt,*args):\nDETAIL= 21\t sys.stderr.write(\"%s - %s\\n\"%(self.address_string(),fmt%args))\nDETAIL= 22\t\nDETAIL= 23\t def reply(self,status,content_type,payload,etag=\"\",include_body=True):\nDETAIL= 24\t self.send_response(status)\nDETAIL= 25\t self.send_header(\"Content-Type\",content_type)\nDETAIL= 26\t self.send_header(\"Content-Length\",str(len(payload)))\nDETAIL= 27\t self.send_header(\"Cache-Control\",\"no-store, no-cache, must-revalidate, max-age=0\")\nDETAIL= 28\t self.send_header(\"Surrogate-Control\",\"no-store\")\nDETAIL= 29\t self.send_header(\"Pragma\",\"no-cache\")\nDETAIL= 30\t self.send_header(\"Expires\",\"0\")\nDETAIL= 31\t self.send_header(\"X-Robots-Tag\",\"noindex, nofollow, noarchive\")\nDETAIL= 32\t if etag:\nDETAIL= 33\t self.send_header(\"ETag\",chr(34)+etag+chr(34))\nDETAIL= 34\t self.send_header(\"X-Homelab-Generation\",etag)\nDETAIL= 35\t self.end_headers()\nDETAIL= 36\t if include_body:\nDETAIL= 37\t self.wfile.write(payload)\nDETAIL= 38\t\nDETAIL= 39\t def do_HEAD(self):\nDETAIL= 40\t self.handle_reader(False)\nDETAIL= 41\t\nDETAIL= 42\t def do_GET(self):\nDETAIL= 43\t self.handle_reader(True)\nDETAIL= 44\t\nDETAIL= 45\t def handle_reader(self,include_body):\nDETAIL= 46\t request_path=self.path.split(\"?\",1)[0]\nDETAIL= 47\t prefix=\"/\"+CAPABILITY+\"/\"\nDETAIL= 48\t if not request_path.startswith(prefix):\nDETAIL= 49\t self.reply(404,\"text/plain; charset=utf-8\",b\"not found\\n\",include_body=include_body)\nDETAIL= 50\t return\nDETAIL= 51\t\nDETAIL= 52\t relative=request_path[len(prefix):]\nDETAIL= 53\t\nDETAIL= 54\t if relative==\"health\":\nDETAIL= 55\t self.reply(200,\"application/json; charset=utf-8\",b\"{\\\"status\\\":\\\"ok\\\",\\\"version\\\":3}\\n\",\"health-v3\",include_body)\nDETAIL= 56\t return\nDETAIL= 57\t\nDETAIL= 58\t if relative==\"feed\":\nDETAIL= 59\t try:\nDETAIL= 60\t payload=MANIFEST.read_bytes()\nDETAIL= 61\t document=json.loads(payload.decode(\"utf-8\"))\nDETAIL= 62\t generation=str(document.get(\"generation\",\"0\"))\nDETAIL= 63\t self.reply(200,\"application/json; charset=utf-8\",payload,generation,include_body)\nDETAIL= 64\t except Exception:\nDETAIL= 65\t self.reply(500,\"text/plain; charset=utf-8\",b\"reader error\\n\",include_body=include_body)\nDETAIL= 66\t return\nDETAIL= 67\t\nDETAIL= 68\t if relative.startswith(\"results/\"):\nDETAIL= 69\t name=relative[len(\"results/\"):]\nDETAIL= 70\t target=RESULTS/name\nDETAIL= 71\t if not RESULT_RE.fullmatch(name) or not target.is_file():\nDETAIL= 72\t self.reply(404,\"text/plain; charset=utf-8\",b\"not found\\n\",include_body=include_body)\nDETAIL= 73\t return\nDETAIL= 74\t try:\nDETAIL= 75\t payload=target.read_bytes()\nDETAIL= 76\t digest=name.rsplit(\"-\",1)[1][:-4]\nDETAIL= 77\t if hashlib.sha256(payload).hexdigest()!=digest:\nDETAIL= 78\t self.reply(500,\"text/plain; charset=utf-8\",b\"integrity error\\n\",include_body=include_body)\nDETAIL= 79\t return\nDETAIL= 80\t self.reply(200,\"text/plain; charset=utf-8\",payload,digest,include_body)\nDETAIL= 81\t except Exception:\nDETAIL= 82\t self.reply(500,\"text/plain; charset=utf-8\",b\"reader error\\n\",include_body=include_body)\nDETAIL= 83\t return\nDETAIL= 84\t\nDETAIL= 85\t self.reply(404,\"text/plain; charset=utf-8\",b\"not found\\n\",include_body=include_body)\nDETAIL= 86\t\nDETAIL= 87\tclass Server(http.server.ThreadingHTTPServer):\nDETAIL= 88\t allow_reuse_address=True\nDETAIL= 89\t\nDETAIL= 90\tServer((\"127.0.0.1\",PORT),Handler).serve_forever()\nCHANGES_MADE=NO\n" +} diff --git a/runtime/history/GITEA-READER-ROUTES-AUDIT-289.txt b/runtime/history/GITEA-READER-ROUTES-AUDIT-289.txt new file mode 100644 index 0000000..ef33bde --- /dev/null +++ b/runtime/history/GITEA-READER-ROUTES-AUDIT-289.txt @@ -0,0 +1,116 @@ +CHAT_OUTPUT_BEGIN +COMMAND_ID=GITEA-READER-ROUTES-AUDIT-289 +STATUS=OK +RC=0 +HOST=pve01 +MODE=read-only +COMPONENT=cluster-audit +REFERENCE_REGISTER_CHECK=OK +REFERENCE_SHA256=5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e +ERROR_REGISTER_CHECK=OK +ERROR_REGISTER_SHA256=08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752 +COMMAND_SHA256=ca1499ccd79985134e654f663bd829da73f07f8aa2af0a227387a97e6c32c66d +DUPLICATE_FAILED_COMMAND_BLOCKED=false +EXECUTION_STARTED=true +CHANGES_MADE=false +SANITIZED=yes +SECRETS_INCLUDED=no +PRIVATE_ADDRESSES_INCLUDED=no +RAW_EVIDENCE_SHA256=8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b +SANITIZED_OUTPUT_SHA256=8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b +OUTPUT_BEGIN +PREVIOUS_COMMAND_ID=GITEA-READER-BIND-AUDIT-288 +DETAIL= 1 #!/usr/bin/python3 +DETAIL= 2 import hashlib +DETAIL= 3 import http.server +DETAIL= 4 import json +DETAIL= 5 import os +DETAIL= 6 import pathlib +DETAIL= 7 import re +DETAIL= 8 import sys +DETAIL= 9 +DETAIL= 10 ROOT=pathlib.Path("/var/lib/homelab-reader-v3") +DETAIL= 11 MANIFEST=ROOT/"manifest.json" +DETAIL= 12 RESULTS=ROOT/"results" +DETAIL= 13 CAPABILITY=os.environ["HOMELAB_READER_CAPABILITY"] +DETAIL= 14 PORT=int(os.environ.get("HOMELAB_READER_PORT","18788")) +DETAIL= 15 RESULT_RE=re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}-[0-9a-f]{64}\.txt$") +DETAIL= 16 +DETAIL= 17 class Handler(http.server.BaseHTTPRequestHandler): +DETAIL= 18 server_version="HomelabReader/3" +DETAIL= 19 +DETAIL= 20 def log_message(self,fmt,*args): +DETAIL= 21 sys.stderr.write("%s - %s\n"%(self.address_string(),fmt%args)) +DETAIL= 22 +DETAIL= 23 def reply(self,status,content_type,payload,etag="",include_body=True): +DETAIL= 24 self.send_response(status) +DETAIL= 25 self.send_header("Content-Type",content_type) +DETAIL= 26 self.send_header("Content-Length",str(len(payload))) +DETAIL= 27 self.send_header("Cache-Control","no-store, no-cache, must-revalidate, max-age=0") +DETAIL= 28 self.send_header("Surrogate-Control","no-store") +DETAIL= 29 self.send_header("Pragma","no-cache") +DETAIL= 30 self.send_header("Expires","0") +DETAIL= 31 self.send_header("X-Robots-Tag","noindex, nofollow, noarchive") +DETAIL= 32 if etag: +DETAIL= 33 self.send_header("ETag",chr(34)+etag+chr(34)) +DETAIL= 34 self.send_header("X-Homelab-Generation",etag) +DETAIL= 35 self.end_headers() +DETAIL= 36 if include_body: +DETAIL= 37 self.wfile.write(payload) +DETAIL= 38 +DETAIL= 39 def do_HEAD(self): +DETAIL= 40 self.handle_reader(False) +DETAIL= 41 +DETAIL= 42 def do_GET(self): +DETAIL= 43 self.handle_reader(True) +DETAIL= 44 +DETAIL= 45 def handle_reader(self,include_body): +DETAIL= 46 request_path=self.path.split("?",1)[0] +DETAIL= 47 prefix="/"+CAPABILITY+"/" +DETAIL= 48 if not request_path.startswith(prefix): +DETAIL= 49 self.reply(404,"text/plain; charset=utf-8",b"not found\n",include_body=include_body) +DETAIL= 50 return +DETAIL= 51 +DETAIL= 52 relative=request_path[len(prefix):] +DETAIL= 53 +DETAIL= 54 if relative=="health": +DETAIL= 55 self.reply(200,"application/json; charset=utf-8",b"{\"status\":\"ok\",\"version\":3}\n","health-v3",include_body) +DETAIL= 56 return +DETAIL= 57 +DETAIL= 58 if relative=="feed": +DETAIL= 59 try: +DETAIL= 60 payload=MANIFEST.read_bytes() +DETAIL= 61 document=json.loads(payload.decode("utf-8")) +DETAIL= 62 generation=str(document.get("generation","0")) +DETAIL= 63 self.reply(200,"application/json; charset=utf-8",payload,generation,include_body) +DETAIL= 64 except Exception: +DETAIL= 65 self.reply(500,"text/plain; charset=utf-8",b"reader error\n",include_body=include_body) +DETAIL= 66 return +DETAIL= 67 +DETAIL= 68 if relative.startswith("results/"): +DETAIL= 69 name=relative[len("results/"):] +DETAIL= 70 target=RESULTS/name +DETAIL= 71 if not RESULT_RE.fullmatch(name) or not target.is_file(): +DETAIL= 72 self.reply(404,"text/plain; charset=utf-8",b"not found\n",include_body=include_body) +DETAIL= 73 return +DETAIL= 74 try: +DETAIL= 75 payload=target.read_bytes() +DETAIL= 76 digest=name.rsplit("-",1)[1][:-4] +DETAIL= 77 if hashlib.sha256(payload).hexdigest()!=digest: +DETAIL= 78 self.reply(500,"text/plain; charset=utf-8",b"integrity error\n",include_body=include_body) +DETAIL= 79 return +DETAIL= 80 self.reply(200,"text/plain; charset=utf-8",payload,digest,include_body) +DETAIL= 81 except Exception: +DETAIL= 82 self.reply(500,"text/plain; charset=utf-8",b"reader error\n",include_body=include_body) +DETAIL= 83 return +DETAIL= 84 +DETAIL= 85 self.reply(404,"text/plain; charset=utf-8",b"not found\n",include_body=include_body) +DETAIL= 86 +DETAIL= 87 class Server(http.server.ThreadingHTTPServer): +DETAIL= 88 allow_reuse_address=True +DETAIL= 89 +DETAIL= 90 Server(("127.0.0.1",PORT),Handler).serve_forever() +CHANGES_MADE=NO + +OUTPUT_END +CHAT_OUTPUT_END diff --git a/runtime/latest.json b/runtime/latest.json index 2e8248e..ea6f3c8 100644 --- a/runtime/latest.json +++ b/runtime/latest.json @@ -1,19 +1,19 @@ { "schema_version": 1, "channel": "homelab-runtime", - "command_id": "GITEA-READER-BIND-AUDIT-288", + "command_id": "GITEA-READER-ROUTES-AUDIT-289", "status": "OK", "rc": 0, "host": "pve01", "mode": "read-only", "component": "cluster-audit", - "started_at_utc": "2026-07-22T21:22:21Z", - "finished_at_utc": "2026-07-22T21:22:21Z", + "started_at_utc": "2026-07-22T21:23:40Z", + "finished_at_utc": "2026-07-22T21:23:40Z", "reference_register_checked": true, "reference_sha256": "5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e", "error_register_checked": true, "error_register_sha256": "08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752", - "command_sha256": "5681d86e9ac1cefab111efb18c135b3cf09dc266fa19e22e78b0e452d582c9e2", + "command_sha256": "ca1499ccd79985134e654f663bd829da73f07f8aa2af0a227387a97e6c32c66d", "duplicate_failed_command_blocked": false, "block_reason": null, "execution_started": true, @@ -22,9 +22,9 @@ "secrets_included": false, "private_addresses_included": false, "raw_evidence_retained_locally": true, - "raw_evidence_sha256": "6b9f5b15b4e54a2d1158b975a4708e706a5669c5cec084ace5088ed0230166fd", - "sanitized_output_sha256": "6b9f5b15b4e54a2d1158b975a4708e706a5669c5cec084ace5088ed0230166fd", + "raw_evidence_sha256": "8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b", + "sanitized_output_sha256": "8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b", "output_truncated_in_json": false, "full_sanitized_output_url": "https://git.gram1.ru/.well-known/homelab-runtime/latest.txt", - "output": "DETAIL=FILE=/usr/local/sbin/homelab-reader-server-v3\nDETAIL=SHA256=86019c63091dbe7ce16beab24012ed779e496a7010798502cde37921b6551f62\nDETAIL=13:CAPABILITY=os.environ[\"HOMELAB_READER_CAPABILITY\"]\nDETAIL=14:PORT=int(os.environ.get(\"HOMELAB_READER_PORT\",\"18788\"))\nDETAIL=87:class Server(http.server.ThreadingHTTPServer):\nDETAIL=90:Server((\"127.0.0.1\",PORT),Handler).serve_forever()\nCHANGES_MADE=NO\n" + "output": "PREVIOUS_COMMAND_ID=GITEA-READER-BIND-AUDIT-288\nDETAIL= 1\t#!/usr/bin/python3\nDETAIL= 2\timport hashlib\nDETAIL= 3\timport http.server\nDETAIL= 4\timport json\nDETAIL= 5\timport os\nDETAIL= 6\timport pathlib\nDETAIL= 7\timport re\nDETAIL= 8\timport sys\nDETAIL= 9\t\nDETAIL= 10\tROOT=pathlib.Path(\"/var/lib/homelab-reader-v3\")\nDETAIL= 11\tMANIFEST=ROOT/\"manifest.json\"\nDETAIL= 12\tRESULTS=ROOT/\"results\"\nDETAIL= 13\tCAPABILITY=os.environ[\"HOMELAB_READER_CAPABILITY\"]\nDETAIL= 14\tPORT=int(os.environ.get(\"HOMELAB_READER_PORT\",\"18788\"))\nDETAIL= 15\tRESULT_RE=re.compile(r\"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}-[0-9a-f]{64}\\.txt$\")\nDETAIL= 16\t\nDETAIL= 17\tclass Handler(http.server.BaseHTTPRequestHandler):\nDETAIL= 18\t server_version=\"HomelabReader/3\"\nDETAIL= 19\t\nDETAIL= 20\t def log_message(self,fmt,*args):\nDETAIL= 21\t sys.stderr.write(\"%s - %s\\n\"%(self.address_string(),fmt%args))\nDETAIL= 22\t\nDETAIL= 23\t def reply(self,status,content_type,payload,etag=\"\",include_body=True):\nDETAIL= 24\t self.send_response(status)\nDETAIL= 25\t self.send_header(\"Content-Type\",content_type)\nDETAIL= 26\t self.send_header(\"Content-Length\",str(len(payload)))\nDETAIL= 27\t self.send_header(\"Cache-Control\",\"no-store, no-cache, must-revalidate, max-age=0\")\nDETAIL= 28\t self.send_header(\"Surrogate-Control\",\"no-store\")\nDETAIL= 29\t self.send_header(\"Pragma\",\"no-cache\")\nDETAIL= 30\t self.send_header(\"Expires\",\"0\")\nDETAIL= 31\t self.send_header(\"X-Robots-Tag\",\"noindex, nofollow, noarchive\")\nDETAIL= 32\t if etag:\nDETAIL= 33\t self.send_header(\"ETag\",chr(34)+etag+chr(34))\nDETAIL= 34\t self.send_header(\"X-Homelab-Generation\",etag)\nDETAIL= 35\t self.end_headers()\nDETAIL= 36\t if include_body:\nDETAIL= 37\t self.wfile.write(payload)\nDETAIL= 38\t\nDETAIL= 39\t def do_HEAD(self):\nDETAIL= 40\t self.handle_reader(False)\nDETAIL= 41\t\nDETAIL= 42\t def do_GET(self):\nDETAIL= 43\t self.handle_reader(True)\nDETAIL= 44\t\nDETAIL= 45\t def handle_reader(self,include_body):\nDETAIL= 46\t request_path=self.path.split(\"?\",1)[0]\nDETAIL= 47\t prefix=\"/\"+CAPABILITY+\"/\"\nDETAIL= 48\t if not request_path.startswith(prefix):\nDETAIL= 49\t self.reply(404,\"text/plain; charset=utf-8\",b\"not found\\n\",include_body=include_body)\nDETAIL= 50\t return\nDETAIL= 51\t\nDETAIL= 52\t relative=request_path[len(prefix):]\nDETAIL= 53\t\nDETAIL= 54\t if relative==\"health\":\nDETAIL= 55\t self.reply(200,\"application/json; charset=utf-8\",b\"{\\\"status\\\":\\\"ok\\\",\\\"version\\\":3}\\n\",\"health-v3\",include_body)\nDETAIL= 56\t return\nDETAIL= 57\t\nDETAIL= 58\t if relative==\"feed\":\nDETAIL= 59\t try:\nDETAIL= 60\t payload=MANIFEST.read_bytes()\nDETAIL= 61\t document=json.loads(payload.decode(\"utf-8\"))\nDETAIL= 62\t generation=str(document.get(\"generation\",\"0\"))\nDETAIL= 63\t self.reply(200,\"application/json; charset=utf-8\",payload,generation,include_body)\nDETAIL= 64\t except Exception:\nDETAIL= 65\t self.reply(500,\"text/plain; charset=utf-8\",b\"reader error\\n\",include_body=include_body)\nDETAIL= 66\t return\nDETAIL= 67\t\nDETAIL= 68\t if relative.startswith(\"results/\"):\nDETAIL= 69\t name=relative[len(\"results/\"):]\nDETAIL= 70\t target=RESULTS/name\nDETAIL= 71\t if not RESULT_RE.fullmatch(name) or not target.is_file():\nDETAIL= 72\t self.reply(404,\"text/plain; charset=utf-8\",b\"not found\\n\",include_body=include_body)\nDETAIL= 73\t return\nDETAIL= 74\t try:\nDETAIL= 75\t payload=target.read_bytes()\nDETAIL= 76\t digest=name.rsplit(\"-\",1)[1][:-4]\nDETAIL= 77\t if hashlib.sha256(payload).hexdigest()!=digest:\nDETAIL= 78\t self.reply(500,\"text/plain; charset=utf-8\",b\"integrity error\\n\",include_body=include_body)\nDETAIL= 79\t return\nDETAIL= 80\t self.reply(200,\"text/plain; charset=utf-8\",payload,digest,include_body)\nDETAIL= 81\t except Exception:\nDETAIL= 82\t self.reply(500,\"text/plain; charset=utf-8\",b\"reader error\\n\",include_body=include_body)\nDETAIL= 83\t return\nDETAIL= 84\t\nDETAIL= 85\t self.reply(404,\"text/plain; charset=utf-8\",b\"not found\\n\",include_body=include_body)\nDETAIL= 86\t\nDETAIL= 87\tclass Server(http.server.ThreadingHTTPServer):\nDETAIL= 88\t allow_reuse_address=True\nDETAIL= 89\t\nDETAIL= 90\tServer((\"127.0.0.1\",PORT),Handler).serve_forever()\nCHANGES_MADE=NO\n" } diff --git a/runtime/latest.txt b/runtime/latest.txt index fd44310..ef33bde 100644 --- a/runtime/latest.txt +++ b/runtime/latest.txt @@ -1,5 +1,5 @@ CHAT_OUTPUT_BEGIN -COMMAND_ID=GITEA-READER-BIND-AUDIT-288 +COMMAND_ID=GITEA-READER-ROUTES-AUDIT-289 STATUS=OK RC=0 HOST=pve01 @@ -9,22 +9,107 @@ REFERENCE_REGISTER_CHECK=OK REFERENCE_SHA256=5763f2f8edc75fcf6f3951f8c95896d1cc112ceccb6cf5b64a383f540a4deb8e ERROR_REGISTER_CHECK=OK ERROR_REGISTER_SHA256=08b3a769b8e38eaa1acec915352fb328180d70caa7ff6cd9d1ec0da426262752 -COMMAND_SHA256=5681d86e9ac1cefab111efb18c135b3cf09dc266fa19e22e78b0e452d582c9e2 +COMMAND_SHA256=ca1499ccd79985134e654f663bd829da73f07f8aa2af0a227387a97e6c32c66d DUPLICATE_FAILED_COMMAND_BLOCKED=false EXECUTION_STARTED=true CHANGES_MADE=false SANITIZED=yes SECRETS_INCLUDED=no PRIVATE_ADDRESSES_INCLUDED=no -RAW_EVIDENCE_SHA256=6b9f5b15b4e54a2d1158b975a4708e706a5669c5cec084ace5088ed0230166fd -SANITIZED_OUTPUT_SHA256=6b9f5b15b4e54a2d1158b975a4708e706a5669c5cec084ace5088ed0230166fd +RAW_EVIDENCE_SHA256=8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b +SANITIZED_OUTPUT_SHA256=8077c2ba090c3c446ffa120fd830682af6b005d086e4263b67ad67eb043d3a9b OUTPUT_BEGIN -DETAIL=FILE=/usr/local/sbin/homelab-reader-server-v3 -DETAIL=SHA256=86019c63091dbe7ce16beab24012ed779e496a7010798502cde37921b6551f62 -DETAIL=13:CAPABILITY=os.environ["HOMELAB_READER_CAPABILITY"] -DETAIL=14:PORT=int(os.environ.get("HOMELAB_READER_PORT","18788")) -DETAIL=87:class Server(http.server.ThreadingHTTPServer): -DETAIL=90:Server(("127.0.0.1",PORT),Handler).serve_forever() +PREVIOUS_COMMAND_ID=GITEA-READER-BIND-AUDIT-288 +DETAIL= 1 #!/usr/bin/python3 +DETAIL= 2 import hashlib +DETAIL= 3 import http.server +DETAIL= 4 import json +DETAIL= 5 import os +DETAIL= 6 import pathlib +DETAIL= 7 import re +DETAIL= 8 import sys +DETAIL= 9 +DETAIL= 10 ROOT=pathlib.Path("/var/lib/homelab-reader-v3") +DETAIL= 11 MANIFEST=ROOT/"manifest.json" +DETAIL= 12 RESULTS=ROOT/"results" +DETAIL= 13 CAPABILITY=os.environ["HOMELAB_READER_CAPABILITY"] +DETAIL= 14 PORT=int(os.environ.get("HOMELAB_READER_PORT","18788")) +DETAIL= 15 RESULT_RE=re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}-[0-9a-f]{64}\.txt$") +DETAIL= 16 +DETAIL= 17 class Handler(http.server.BaseHTTPRequestHandler): +DETAIL= 18 server_version="HomelabReader/3" +DETAIL= 19 +DETAIL= 20 def log_message(self,fmt,*args): +DETAIL= 21 sys.stderr.write("%s - %s\n"%(self.address_string(),fmt%args)) +DETAIL= 22 +DETAIL= 23 def reply(self,status,content_type,payload,etag="",include_body=True): +DETAIL= 24 self.send_response(status) +DETAIL= 25 self.send_header("Content-Type",content_type) +DETAIL= 26 self.send_header("Content-Length",str(len(payload))) +DETAIL= 27 self.send_header("Cache-Control","no-store, no-cache, must-revalidate, max-age=0") +DETAIL= 28 self.send_header("Surrogate-Control","no-store") +DETAIL= 29 self.send_header("Pragma","no-cache") +DETAIL= 30 self.send_header("Expires","0") +DETAIL= 31 self.send_header("X-Robots-Tag","noindex, nofollow, noarchive") +DETAIL= 32 if etag: +DETAIL= 33 self.send_header("ETag",chr(34)+etag+chr(34)) +DETAIL= 34 self.send_header("X-Homelab-Generation",etag) +DETAIL= 35 self.end_headers() +DETAIL= 36 if include_body: +DETAIL= 37 self.wfile.write(payload) +DETAIL= 38 +DETAIL= 39 def do_HEAD(self): +DETAIL= 40 self.handle_reader(False) +DETAIL= 41 +DETAIL= 42 def do_GET(self): +DETAIL= 43 self.handle_reader(True) +DETAIL= 44 +DETAIL= 45 def handle_reader(self,include_body): +DETAIL= 46 request_path=self.path.split("?",1)[0] +DETAIL= 47 prefix="/"+CAPABILITY+"/" +DETAIL= 48 if not request_path.startswith(prefix): +DETAIL= 49 self.reply(404,"text/plain; charset=utf-8",b"not found\n",include_body=include_body) +DETAIL= 50 return +DETAIL= 51 +DETAIL= 52 relative=request_path[len(prefix):] +DETAIL= 53 +DETAIL= 54 if relative=="health": +DETAIL= 55 self.reply(200,"application/json; charset=utf-8",b"{\"status\":\"ok\",\"version\":3}\n","health-v3",include_body) +DETAIL= 56 return +DETAIL= 57 +DETAIL= 58 if relative=="feed": +DETAIL= 59 try: +DETAIL= 60 payload=MANIFEST.read_bytes() +DETAIL= 61 document=json.loads(payload.decode("utf-8")) +DETAIL= 62 generation=str(document.get("generation","0")) +DETAIL= 63 self.reply(200,"application/json; charset=utf-8",payload,generation,include_body) +DETAIL= 64 except Exception: +DETAIL= 65 self.reply(500,"text/plain; charset=utf-8",b"reader error\n",include_body=include_body) +DETAIL= 66 return +DETAIL= 67 +DETAIL= 68 if relative.startswith("results/"): +DETAIL= 69 name=relative[len("results/"):] +DETAIL= 70 target=RESULTS/name +DETAIL= 71 if not RESULT_RE.fullmatch(name) or not target.is_file(): +DETAIL= 72 self.reply(404,"text/plain; charset=utf-8",b"not found\n",include_body=include_body) +DETAIL= 73 return +DETAIL= 74 try: +DETAIL= 75 payload=target.read_bytes() +DETAIL= 76 digest=name.rsplit("-",1)[1][:-4] +DETAIL= 77 if hashlib.sha256(payload).hexdigest()!=digest: +DETAIL= 78 self.reply(500,"text/plain; charset=utf-8",b"integrity error\n",include_body=include_body) +DETAIL= 79 return +DETAIL= 80 self.reply(200,"text/plain; charset=utf-8",payload,digest,include_body) +DETAIL= 81 except Exception: +DETAIL= 82 self.reply(500,"text/plain; charset=utf-8",b"reader error\n",include_body=include_body) +DETAIL= 83 return +DETAIL= 84 +DETAIL= 85 self.reply(404,"text/plain; charset=utf-8",b"not found\n",include_body=include_body) +DETAIL= 86 +DETAIL= 87 class Server(http.server.ThreadingHTTPServer): +DETAIL= 88 allow_reuse_address=True +DETAIL= 89 +DETAIL= 90 Server(("127.0.0.1",PORT),Handler).serve_forever() CHANGES_MADE=NO OUTPUT_END