Skip to content

Commit fc33572

Browse files
committed
feat - Increase parity with gcpwn, make dependenices optional, add more enumeration, add exploit modules, add delegation auth
1 parent 202e951 commit fc33572

371 files changed

Lines changed: 36931 additions & 14863 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/dependabot.yml

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,44 @@
11
version: 2
22
updates:
3+
# Python dependencies (requirements.txt + the optional [dev]/[excel] extras).
4+
# Routine minor/patch bumps are BATCHED into grouped PRs to cut review churn;
5+
# MAJOR bumps intentionally fall out of the groups into their own PRs so a
6+
# potentially breaking change (e.g. a new oci or pandas major) gets scrutiny.
37
- package-ecosystem: "pip"
48
directory: "/"
59
schedule:
610
interval: "weekly"
711
open-pull-requests-limit: 10
812
labels:
913
- "dependencies"
14+
groups:
15+
oci:
16+
patterns:
17+
- "oci"
18+
- "oci-*"
19+
update-types:
20+
- "minor"
21+
- "patch"
22+
python-deps:
23+
patterns:
24+
- "*"
25+
exclude-patterns:
26+
- "oci"
27+
- "oci-*"
28+
update-types:
29+
- "minor"
30+
- "patch"
1031

32+
# Pinned GitHub Actions (Dependabot bumps the commit SHA + version comment).
33+
# Action updates are low-risk, so batch them all into one PR.
1134
- package-ecosystem: "github-actions"
1235
directory: "/"
1336
schedule:
1437
interval: "weekly"
15-
open-pull-requests-limit: 10
38+
open-pull-requests-limit: 5
1639
labels:
1740
- "dependencies"
41+
groups:
42+
github-actions:
43+
patterns:
44+
- "*"
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
name: Build Release Executables
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
run_tests:
7+
description: "Run unit tests before building binaries"
8+
required: true
9+
type: boolean
10+
default: true
11+
release_tag:
12+
description: "Release tag to create/update (example: v0.5.2)"
13+
required: true
14+
type: string
15+
release_title:
16+
description: "Release title shown on GitHub Releases page"
17+
required: true
18+
type: string
19+
release_notes:
20+
description: "Release notes/body text for the GitHub Release"
21+
required: true
22+
type: string
23+
build_linux:
24+
description: "Build Linux binary (ubuntu-latest)"
25+
required: true
26+
type: boolean
27+
default: true
28+
build_macos:
29+
description: "Build macOS binary (macos-latest)"
30+
required: true
31+
type: boolean
32+
default: true
33+
build_windows:
34+
description: "Build Windows binary (windows-latest)"
35+
required: true
36+
type: boolean
37+
default: true
38+
39+
# Least privilege by default; only the jobs that publish release assets opt up to write.
40+
permissions:
41+
contents: read
42+
43+
concurrency:
44+
group: build-release-${{ github.ref_name }}
45+
cancel-in-progress: false
46+
47+
# Every job re-asserts the same authorization guard (GitHub Actions has no shared
48+
# anchors) as defense-in-depth: only WebbinRoot, on NetSPI/OCInferno, on refs/heads/main.
49+
# Downstream jobs also chain via `needs`, so an unauthorized run stops at preflight.
50+
jobs:
51+
preflight-tests:
52+
if: >-
53+
github.event_name == 'workflow_dispatch' &&
54+
github.repository == 'NetSPI/OCInferno' &&
55+
github.actor == 'WebbinRoot' &&
56+
github.triggering_actor == 'WebbinRoot' &&
57+
github.ref == 'refs/heads/main'
58+
runs-on: ubuntu-latest
59+
timeout-minutes: 20
60+
steps:
61+
- name: Checkout
62+
# v7.0.0
63+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
64+
with:
65+
persist-credentials: false
66+
67+
- name: Setup Python
68+
# v6.3.0
69+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
70+
with:
71+
python-version: "3.12"
72+
cache: pip
73+
cache-dependency-path: pyproject.toml
74+
75+
# Job always runs when authorized so downstream can use plain `needs`; the
76+
# tests themselves are what run_tests toggles. A no-op job here still succeeds.
77+
- name: Install dependencies
78+
if: inputs.run_tests
79+
run: python -m pip install .[dev]
80+
81+
- name: Run unit tests
82+
if: inputs.run_tests
83+
run: python -m pytest -q -ra tests/unit tests/module_contracts
84+
85+
resolve-build-targets:
86+
if: >-
87+
github.event_name == 'workflow_dispatch' &&
88+
github.repository == 'NetSPI/OCInferno' &&
89+
github.actor == 'WebbinRoot' &&
90+
github.triggering_actor == 'WebbinRoot' &&
91+
github.ref == 'refs/heads/main'
92+
needs: preflight-tests
93+
runs-on: ubuntu-latest
94+
timeout-minutes: 5
95+
outputs:
96+
os_matrix: ${{ steps.set-matrix.outputs.os_matrix }}
97+
steps:
98+
- name: Resolve selected OS targets
99+
id: set-matrix
100+
shell: bash
101+
run: |
102+
sel=()
103+
if [ "${{ inputs.build_linux }}" = "true" ]; then sel+=('"ubuntu-latest"'); fi
104+
if [ "${{ inputs.build_macos }}" = "true" ]; then sel+=('"macos-latest"'); fi
105+
if [ "${{ inputs.build_windows }}" = "true" ]; then sel+=('"windows-latest"'); fi
106+
if [ ${#sel[@]} -eq 0 ]; then
107+
echo "Select at least one build target OS (linux/macos/windows)." >&2
108+
exit 1
109+
fi
110+
IFS=,
111+
echo "os_matrix=[${sel[*]}]" >> "$GITHUB_OUTPUT"
112+
echo "Resolved build targets: [${sel[*]}]"
113+
114+
# Manual gate + release creation in one job: the protected `release` environment must
115+
# be approved before this job runs, so the approval directly gates the contents:write.
116+
# Fails closed if that environment exists without protection rules, then creates/updates
117+
# the GitHub Release.
118+
create-release:
119+
if: >-
120+
github.event_name == 'workflow_dispatch' &&
121+
github.repository == 'NetSPI/OCInferno' &&
122+
github.actor == 'WebbinRoot' &&
123+
github.triggering_actor == 'WebbinRoot' &&
124+
github.ref == 'refs/heads/main'
125+
needs: resolve-build-targets
126+
runs-on: ubuntu-latest
127+
timeout-minutes: 10
128+
environment:
129+
name: release
130+
permissions:
131+
contents: write
132+
steps:
133+
- name: Assert protected release environment
134+
shell: bash
135+
env:
136+
GH_TOKEN: ${{ github.token }}
137+
run: |
138+
# Guard against implicit, unprotected environment creation: if 'release'
139+
# exists without protection rules, fail closed (no approval actually gated us).
140+
RULE_COUNT="$(gh api "repos/${GITHUB_REPOSITORY}/environments/release" --jq '.protection_rules | length')"
141+
if [ "${RULE_COUNT}" -lt 1 ]; then
142+
echo "Release environment exists but has no protection rules configured."
143+
exit 1
144+
fi
145+
146+
- name: Create or update GitHub Release
147+
shell: bash
148+
env:
149+
GH_TOKEN: ${{ github.token }}
150+
GH_REPO: ${{ github.repository }}
151+
RELEASE_TAG: ${{ inputs.release_tag }}
152+
RELEASE_TITLE: ${{ inputs.release_title }}
153+
RELEASE_NOTES: ${{ inputs.release_notes }}
154+
RELEASE_TARGET: ${{ github.sha }}
155+
run: |
156+
if gh release view "$RELEASE_TAG" >/dev/null 2>&1; then
157+
gh release edit "$RELEASE_TAG" --title "$RELEASE_TITLE" --notes "$RELEASE_NOTES"
158+
else
159+
gh release create "$RELEASE_TAG" --target "$RELEASE_TARGET" --title "$RELEASE_TITLE" --notes "$RELEASE_NOTES"
160+
fi
161+
162+
build-executables:
163+
if: >-
164+
github.event_name == 'workflow_dispatch' &&
165+
github.repository == 'NetSPI/OCInferno' &&
166+
github.actor == 'WebbinRoot' &&
167+
github.triggering_actor == 'WebbinRoot' &&
168+
github.ref == 'refs/heads/main'
169+
needs:
170+
- create-release
171+
- resolve-build-targets
172+
strategy:
173+
fail-fast: false
174+
matrix:
175+
os: ${{ fromJSON(needs.resolve-build-targets.outputs.os_matrix) }}
176+
variant:
177+
- { label: base, extras: "" }
178+
- { label: table, extras: "table" }
179+
- { label: excel, extras: "excel" }
180+
- { label: table_excel, extras: "table,excel" }
181+
name: Build (${{ matrix.os }} | ${{ matrix.variant.label }})
182+
runs-on: ${{ matrix.os }}
183+
timeout-minutes: 35
184+
permissions:
185+
contents: write
186+
steps:
187+
- name: Checkout
188+
# v7.0.0
189+
uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0
190+
with:
191+
persist-credentials: false
192+
193+
- name: Setup Python
194+
# v6.3.0
195+
uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1
196+
with:
197+
python-version: "3.12"
198+
cache: pip
199+
cache-dependency-path: pyproject.toml
200+
201+
- name: Install dependencies
202+
shell: bash
203+
run: |
204+
EXTRAS="${{ matrix.variant.extras }}"
205+
if [ -n "$EXTRAS" ]; then
206+
python -m pip install ".[${EXTRAS}]" pyinstaller==6.21.0
207+
else
208+
python -m pip install . pyinstaller==6.21.0
209+
fi
210+
211+
- name: Build executable
212+
shell: bash
213+
run: |
214+
# OCInferno's per-service modules are imported DYNAMICALLY by dotted path
215+
# (from mappings/module_mappings.json) and live in PEP-420 namespace packages
216+
# (only ocinferno/__init__.py, ocinferno/mappings/__init__.py, and one
217+
# opengraph helpers/__init__.py exist). PyInstaller's static analysis and
218+
# --collect-submodules CANNOT discover them, so we enumerate the full module
219+
# set by walking the source tree and pass each as --hidden-import. Importing
220+
# each also pulls in the third-party deps it uses (e.g. the oci SDK).
221+
# --onefile: single-file executable. --collect-data ocinferno: bundle the
222+
# runtime data files (the mappings JSON + any per-module data).
223+
MODS="$RUNNER_TEMP/ocinferno_hidden_imports.txt"
224+
python - <<'PY' > "$MODS"
225+
from pathlib import Path
226+
names = {"ocinferno"}
227+
for py in Path("ocinferno").rglob("*.py"):
228+
if "__pycache__" in py.parts:
229+
continue
230+
parts = list(py.relative_to("ocinferno").with_suffix("").parts)
231+
if parts and parts[-1] == "__init__":
232+
parts = parts[:-1]
233+
names.add(".".join(["ocinferno", *parts]))
234+
print("\n".join(sorted(names)))
235+
PY
236+
HIDDEN_ARGS=()
237+
while IFS= read -r mod; do
238+
HIDDEN_ARGS+=(--hidden-import "$mod")
239+
done < "$MODS"
240+
echo "Collected $(( ${#HIDDEN_ARGS[@]} / 2 )) ocinferno hidden-import modules"
241+
python -m PyInstaller \
242+
--noconfirm \
243+
--clean \
244+
--onefile \
245+
--name ocinferno \
246+
--collect-data ocinferno \
247+
"${HIDDEN_ARGS[@]}" \
248+
ocinferno/__main__.py
249+
250+
- name: Verify and upload binary to the release
251+
shell: bash
252+
env:
253+
GH_TOKEN: ${{ github.token }}
254+
GH_REPO: ${{ github.repository }}
255+
RELEASE_TAG: ${{ inputs.release_tag }}
256+
run: |
257+
# PyInstaller appends .exe on Windows; dist/ocinferno elsewhere.
258+
EXT=""
259+
if [ "$RUNNER_OS" = "Windows" ]; then EXT=".exe"; fi
260+
BIN="dist/ocinferno${EXT}"
261+
test -f "$BIN"
262+
"$BIN" --help >/dev/null
263+
ASSET="ocinferno-${{ matrix.os }}-${{ matrix.variant.label }}${EXT}"
264+
cp "$BIN" "$ASSET"
265+
gh release upload "$RELEASE_TAG" "$ASSET" --clobber
266+
echo "Uploaded $ASSET"

0 commit comments

Comments
 (0)