audit-labs/audit-tools

A collection of scripts, queries, and other goodies you can use in an audit.

clone: git clone https://gitbay.org/audit-labs/audit-tools.git

d62f25007470fe546e0f9d2e38a26e84146f72c5

signed_unknown_key

author: Christian Cleberg <hello@cleberg.net> · 2025-05-07T01:49:19Z
committer: <noreply@github.com>

add gitlab pipelines.py script (#5)

* add gitlab pipelines.py script

* Commit from GitHub Actions (Ruff)

---------

Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
 applications/gitlab/README.org      | 33 +++++++++++++++++++++
 applications/gitlab/pipelines.py    | 59 +++++++++++++++++++++++++++++++++++++
 applications/gitlab/repositories.py | 14 +++++++--
 3 files changed, 103 insertions(+), 3 deletions(-)

diff --git a/applications/gitlab/README.org b/applications/gitlab/README.org
index 2afc363..d137497 100644
--- a/applications/gitlab/README.org
+++ b/applications/gitlab/README.org
@@ -69,6 +69,39 @@ python ./passwords.py
 # TODO: Need access to a self-hosted version of GitLab to test this out.
 #+end_src
 
+* =pipelines.py=
+
+#+begin_src sh
+python ./pipelines.py
+#+end_src
+
+#+begin_src text
+Pipeline ID: 1754222228
+  Status: failed
+  Ref: master
+  Created At: 2025-04-06T03:39:15.065Z
+  Duration: N/A seconds
+  Configuration: N/A
+Pipeline ID: 1754221831
+  Status: failed
+  Ref: pr-1
+  Created At: 2025-04-06T03:37:42.333Z
+  Duration: N/A seconds
+  Configuration: N/A
+Pipeline ID: 1754220271
+  Status: failed
+  Ref: pr-1
+  Created At: 2025-04-06T03:33:38.606Z
+  Duration: N/A seconds
+  Configuration: N/A
+Pipeline ID: 1754214637
+  Status: failed
+  Ref: master
+  Created At: 2025-04-06T03:21:39.902Z
+  Duration: N/A seconds
+  Configuration: N/A
+#+end_src
+
 * =provisioning.py=
 
 \*This script requires an active Premium or Ultimate subscription.*\
diff --git a/applications/gitlab/pipelines.py b/applications/gitlab/pipelines.py
new file mode 100644
index 0000000..cd333f6
--- /dev/null
+++ b/applications/gitlab/pipelines.py
@@ -0,0 +1,59 @@
+"""
+Review CI/CD pipelines and their configurations for a specific GitLab project.
+"""
+
+import requests
+
+BASE_URL = "https://gitlab.com/api/v4"
+PRIVATE_TOKEN = "your_access_token"
+PROJECT_ID = "project_id"
+TIMEOUT = 30
+
+HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
+
+if __name__ == "__main__":
+    page = 1
+    per_page = 100
+
+    while True:
+        response = requests.get(
+            f"{BASE_URL}/projects/{PROJECT_ID}/pipelines",
+            headers=HEADERS,
+            params={"page": page, "per_page": per_page},
+            timeout=TIMEOUT,
+        )
+        if response.status_code == 200:
+            pipelines = response.json()
+            if not pipelines:
+                break
+
+            for pipeline in pipelines:
+                pipeline_id = pipeline["id"]
+                status = pipeline["status"]
+                ref = pipeline["ref"]
+                created_at = pipeline["created_at"]
+                duration = pipeline.get("duration", "N/A")
+
+                print(f"Pipeline ID: {pipeline_id}")
+                print(f"  Status: {status}")
+                print(f"  Ref: {ref}")
+                print(f"  Created At: {created_at}")
+                print(f"  Duration: {duration} seconds")
+
+                detail_response = requests.get(
+                    f"{BASE_URL}/projects/{PROJECT_ID}/pipelines/{pipeline_id}",
+                    headers=HEADERS,
+                    timeout=TIMEOUT,
+                )
+                if detail_response.status_code == 200:
+                    pipeline_details = detail_response.json()
+                    print(f"  Configuration: {pipeline_details.get('config', 'N/A')}")
+                else:
+                    print(
+                        f"  Failed to fetch pipeline details: {detail_response.status_code}, {detail_response.text}"
+                    )
+
+            page += 1
+        else:
+            print(f"Failed to fetch pipelines: {response.status_code}, {response.text}")
+            break
diff --git a/applications/gitlab/repositories.py b/applications/gitlab/repositories.py
index a955a49..8281598 100644
--- a/applications/gitlab/repositories.py
+++ b/applications/gitlab/repositories.py
@@ -9,7 +9,7 @@ PRIVATE_TOKEN = "your_access_token"
 USER_ID = "your_user_or_group_id"
 TIMEOUT = 30
 
-URL = f"{BASE_URL}/groups/{USER_ID}/projects" # Group URL
+URL = f"{BASE_URL}/groups/{USER_ID}/projects"  # Group URL
 # URL = f"{BASE_URL}/users/{USER_ID}/projects" # User URL
 HEADERS = {"PRIVATE-TOKEN": PRIVATE_TOKEN}
 
@@ -20,7 +20,12 @@ def list_projects(user_or_group_id):
     projects = []
 
     while True:
-        response = requests.get(URL, headers=HEADERS, timeout=TIMEOUT, params={"page": page, "per_page": PER_PAGE})
+        response = requests.get(
+            URL,
+            headers=HEADERS,
+            timeout=TIMEOUT,
+            params={"page": page, "per_page": PER_PAGE},
+        )
 
         if response.status_code == 200:
             current_projects = response.json()
@@ -29,7 +34,9 @@ def list_projects(user_or_group_id):
             projects.extend(current_projects)
             page += 1
         else:
-            print(f"Failed to retrieve projects: {response.status_code} - {response.text}")
+            print(
+                f"Failed to retrieve projects: {response.status_code} - {response.text}"
+            )
             break
 
     if projects:
@@ -39,5 +46,6 @@ def list_projects(user_or_group_id):
     else:
         print(f"No projects found for ID: {user_or_group_id}.")
 
+
 if __name__ == "__main__":
     list_projects(USER_ID)