Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
230 changes: 189 additions & 41 deletions .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,63 @@ name: Build Release Packages
on:
release:
types: [published]
pull_request:
types: [labeled, synchronize]
Comment on lines +6 to +7
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat .github/workflows/build-release.yml

Repository: HudsonGraeme/MoCI

Length of output: 9362


🌐 Web query:

GitHub GITHUB_TOKEN permissions pull request same repository forked PR write access

💡 Result:

By default, the GITHUB_TOKEN in GitHub Actions workflows triggered by a pull_request event from a forked repository has read-only permissions to the base repository. This is a security feature to prevent malicious forks from writing to the target repo using workflows. Write access with GITHUB_TOKEN for forked PRs requires one of these: 1. Use the pull_request_target event trigger instead of pull_request. This grants read/write permissions to GITHUB_TOKEN (configurable via the permissions key) even for forks, and workflows run from the base branch. However, it has security risks: do not checkout the PR head sha or run untrusted code, as it can access secrets. See GitHub security guides for safe usage. 2. For private repositories, enable "Send write tokens to workflows from pull requests" in repo Actions settings (under Fork pull request workflows in private repositories). This allows GITHUB_TOKEN write permissions for pull_request events from forks, but pair with "Require approval for fork pull request workflows" for safety. GITHUB_TOKEN cannot write to the fork repository itself from the base repo workflow; it only has permissions scoped to the base repo. Permissions can be set in the workflow YAML with the 'permissions' key, e.g.: permissions: contents: write pull-requests: write Repo settings also control default GITHUB_TOKEN permissions (read-only or read/write).

Citations:


Restrict token scope for pull request jobs.

The workflow uses pull_request triggers with top-level permissions: contents: write. For same-repository PRs, this write scope is not automatically downgraded (unlike forked PRs). While the current code properly gates release uploads with if: github.event_name == 'release', the write-scoped token remains available in the job environment during PR runs, creating unnecessary exposure.

Add job-level permissions to restrict token scope for PR execution:

Example fix
build-ipk:
  permissions:
    contents: ${{ github.event_name == 'release' && 'write' || 'read' }}

Or simplify by setting read for matrix jobs and keeping write only in a separate release-publishing step.

Also applies to: build-apk job (same concern applies)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-release.yml around lines 6 - 7, The workflow grants
top-level write permissions to contents during pull_request runs, exposing a
write-scoped token; update the job definitions for build-ipk and build-apk to
add job-level permissions that restrict token scope during PRs (e.g., set
permissions.contents to read by default and only enable write when
github.event_name == 'release' or move write to a separate release-publish job);
modify the build-ipk and build-apk job blocks to include a permissions stanza
that conditionally sets contents to read or to write only for release events so
PR runs receive a read-only token.


permissions:
contents: write
contents: read

jobs:
build:
name: Build ${{ matrix.target }}
prepare:
name: Build assets
if: github.event_name == 'release' || github.event.label.name == 'run-build' || (github.event_name == 'pull_request' && github.event.action == 'synchronize' && contains(github.event.pull_request.labels.*.name, 'run-build'))
runs-on: ubuntu-latest
outputs:
version: ${{ steps.version.outputs.version }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 9

- name: Install Node dependencies
run: pnpm install --frozen-lockfile

- name: Build minified assets
run: pnpm run build

- name: Determine version
id: version
run: |
if [ "${{ github.event_name }}" = "release" ]; then
echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
echo "version=0.0.0.${{ github.event.pull_request.number }}" >> $GITHUB_OUTPUT
fi

- name: Upload dist
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 1

build-ipk:
name: Build ipk ${{ matrix.arch }}
needs: prepare
runs-on: ubuntu-latest
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- target: x86/64
Expand Down Expand Up @@ -43,83 +91,183 @@ jobs:
- name: Checkout
uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v4
- name: Download dist
uses: actions/download-artifact@v4
with:
node-version: '20'
name: dist
path: dist/

- name: Setup pnpm
uses: pnpm/action-setup@v4
- name: Cache SDK
id: cache-sdk
uses: actions/cache@v4
with:
version: 9

- name: Install Node dependencies
run: pnpm install --frozen-lockfile

- name: Build minified assets
run: pnpm run build
path: ${{ matrix.sdk_name }}
key: sdk-${{ matrix.sdk_name }}

Comment on lines +100 to 106
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cd "$(git rev-parse --show-toplevel)" && find . -name "build-release.yml" -type f

Repository: HudsonGraeme/MoCI

Length of output: 98


🏁 Script executed:

cd "$(git rev-parse --show-toplevel)" && cat -n .github/workflows/build-release.yml | head -n 260

Repository: HudsonGraeme/MoCI

Length of output: 10976


The SDK cache can publish stale packages.

The cache step persists the entire SDK directory, including bin/packages/. When a cached SDK is reused, old build outputs remain. The "Find and rename" steps use head -n 1 to select the first matching file, which is non-deterministic and can pick an older package if multiple versions exist. This risks uploading a stale package to the release instead of the current build output.

Clean prior package outputs before each build and use exact filename matching:

Suggested hardening
       - name: Build ipk package
         working-directory: ${{ matrix.sdk_name }}
         run: |
+          find bin/packages -name 'moci_*.ipk' -delete
           make defconfig
           make package/moci/compile V=s

       - name: Find and rename ipk
         id: find_ipk
         working-directory: ${{ matrix.sdk_name }}
         run: |
+          VERSION="${{ needs.prepare.outputs.version }}"
-          IPK_FILE=$(find bin/packages -name "moci_*.ipk" | head -n 1)
+          IPK_FILE=$(find bin/packages -name "moci_${VERSION}-r1_${{ matrix.arch }}.ipk" -print -quit)
           if [ -z "$IPK_FILE" ]; then
             echo "Error: ipk package not found!"
             exit 1
           fi
-          VERSION="${{ needs.prepare.outputs.version }}"
           NEW_NAME="moci_${VERSION}-r1_${{ matrix.arch }}.ipk"

       - name: Build apk package
         working-directory: ${{ matrix.sdk_name }}
         run: |
+          find bin/packages -name 'moci-*.apk' -delete
           make defconfig
           make package/moci/compile V=s

       - name: Find and rename apk
         id: find_apk
         working-directory: ${{ matrix.sdk_name }}
         run: |
+          VERSION="${{ needs.prepare.outputs.version }}"
-          APK_FILE=$(find bin/packages -name "moci*.apk" | head -n 1)
+          APK_FILE=$(find bin/packages -name "moci-${VERSION}-r1.apk" -print -quit)
           if [ -z "$APK_FILE" ]; then
             echo "Error: apk package not found!"
             exit 1
           fi
-          VERSION="${{ needs.prepare.outputs.version }}"
           NEW_NAME="moci_${VERSION}-r1_${{ matrix.arch }}.apk"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.github/workflows/build-release.yml around lines 97 - 103, The SDK cache
step ("Cache SDK" with id cache-sdk using actions/cache@v4 and matrix.sdk_name)
currently persists the entire SDK including bin/packages which can leave old
build outputs; modify the workflow to (1) clean the SDK's prior build outputs
(remove or empty bin/packages) before building so cached stale packages can't be
reused, and (2) change the "Find and rename" logic to avoid head -n 1 by
matching the exact expected filename (or using a deterministic pattern with
sort/most-recent selection) when selecting the package to upload; alternatively
restrict the cache path to exclude bin/packages (or explicitly cache only source
artifacts) to prevent packaging artifacts from being cached.

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y zstd

- name: Download SDK
- name: Download and extract SDK
if: steps.cache-sdk.outputs.cache-hit != 'true'
run: |
SDK_URL="https://downloads.openwrt.org/releases/24.10.5/targets/${{ matrix.target }}/${{ matrix.sdk_name }}.tar.zst"
echo "Downloading SDK from: $SDK_URL"
curl -L -o sdk.tar.zst "$SDK_URL"

- name: Extract SDK
run: |
tar --use-compress-program=unzstd -xf sdk.tar.zst
rm sdk.tar.zst

- name: Copy package files
run: |
VERSION=${GITHUB_REF#refs/tags/}
VERSION="${{ needs.prepare.outputs.version }}"
mkdir -p ${{ matrix.sdk_name }}/package/moci
sed "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" Makefile > ${{ matrix.sdk_name }}/package/moci/Makefile
cp -r dist ${{ matrix.sdk_name }}/package/moci/
cp rpcd-acl.json ${{ matrix.sdk_name }}/package/moci/
cp -r files ${{ matrix.sdk_name }}/package/moci/

- name: Update feeds
if: steps.cache-sdk.outputs.cache-hit != 'true'
working-directory: ${{ matrix.sdk_name }}
run: |
./scripts/feeds update -a
./scripts/feeds install -a

- name: Configure SDK
- name: Build ipk package
working-directory: ${{ matrix.sdk_name }}
run: |
rm -rf bin/packages
make defconfig

- name: Build package
working-directory: ${{ matrix.sdk_name }}
run: |
make package/moci/compile V=s

- name: Find package
id: find_package
- name: Find and rename ipk
id: find_ipk
working-directory: ${{ matrix.sdk_name }}
run: |
IPK_FILE=$(find bin/packages -name "moci_*.ipk" | head -n 1)
if [ -z "$IPK_FILE" ]; then
echo "Error: Package not found!"
echo "Error: ipk package not found!"
exit 1
fi
echo "package_path=$IPK_FILE" >> $GITHUB_OUTPUT
VERSION=${GITHUB_REF#refs/tags/}
ARCH_SUFFIX="${{ matrix.arch }}"
NEW_NAME="moci_${VERSION}-r1_${ARCH_SUFFIX}.ipk"
VERSION="${{ needs.prepare.outputs.version }}"
NEW_NAME="moci_${VERSION}-r1_${{ matrix.arch }}.ipk"
mv "$IPK_FILE" "/tmp/$NEW_NAME"
echo "package_name=$NEW_NAME" >> $GITHUB_OUTPUT

- name: Rename package
- name: Upload to release
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: /tmp/${{ steps.find_ipk.outputs.package_name }}

- name: Upload artifact
if: github.event_name != 'release'
uses: actions/upload-artifact@v4
with:
name: moci-ipk-${{ matrix.arch }}
path: /tmp/${{ steps.find_ipk.outputs.package_name }}

build-apk:
name: Build apk ${{ matrix.arch }}
needs: prepare
runs-on: ubuntu-latest
permissions:
contents: write
strategy:
fail-fast: false
matrix:
include:
- target: x86/64
arch: x86_64
sdk_name: openwrt-sdk-25.12.0-x86-64_gcc-14.3.0_musl.Linux-x86_64
- target: ramips/mt7621
arch: mipsel_24kc
sdk_name: openwrt-sdk-25.12.0-ramips-mt7621_gcc-14.3.0_musl.Linux-x86_64
- target: ath79/generic
arch: mips_24kc
sdk_name: openwrt-sdk-25.12.0-ath79-generic_gcc-14.3.0_musl.Linux-x86_64
- target: mediatek/filogic
arch: aarch64_cortex-a53
sdk_name: openwrt-sdk-25.12.0-mediatek-filogic_gcc-14.3.0_musl.Linux-x86_64
- target: bcm27xx/bcm2711
arch: aarch64_cortex-a72
sdk_name: openwrt-sdk-25.12.0-bcm27xx-bcm2711_gcc-14.3.0_musl.Linux-x86_64
- target: ipq40xx/generic
arch: arm_cortex-a7_neon-vfpv4
sdk_name: openwrt-sdk-25.12.0-ipq40xx-generic_gcc-14.3.0_musl_eabi.Linux-x86_64
- target: mvebu/cortexa9
arch: arm_cortex-a9_vfpv3-d16
sdk_name: openwrt-sdk-25.12.0-mvebu-cortexa9_gcc-14.3.0_musl_eabi.Linux-x86_64
- target: ipq806x/generic
arch: arm_cortex-a15_neon-vfpv4
sdk_name: openwrt-sdk-25.12.0-ipq806x-generic_gcc-14.3.0_musl_eabi.Linux-x86_64

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Download dist
uses: actions/download-artifact@v4
with:
name: dist
path: dist/

- name: Cache SDK
id: cache-sdk
uses: actions/cache@v4
with:
path: ${{ matrix.sdk_name }}
key: sdk-${{ matrix.sdk_name }}

- name: Download and extract SDK
if: steps.cache-sdk.outputs.cache-hit != 'true'
run: |
SDK_URL="https://downloads.openwrt.org/releases/25.12.0/targets/${{ matrix.target }}/${{ matrix.sdk_name }}.tar.zst"
curl -L -o sdk.tar.zst "$SDK_URL"
tar --use-compress-program=unzstd -xf sdk.tar.zst
rm sdk.tar.zst

- name: Copy package files
run: |
VERSION="${{ needs.prepare.outputs.version }}"
mkdir -p ${{ matrix.sdk_name }}/package/moci
sed "s/PKG_VERSION:=.*/PKG_VERSION:=$VERSION/" Makefile > ${{ matrix.sdk_name }}/package/moci/Makefile
cp -r dist ${{ matrix.sdk_name }}/package/moci/
cp rpcd-acl.json ${{ matrix.sdk_name }}/package/moci/
cp -r files ${{ matrix.sdk_name }}/package/moci/

- name: Update feeds
if: steps.cache-sdk.outputs.cache-hit != 'true'
working-directory: ${{ matrix.sdk_name }}
run: |
mv ${{ steps.find_package.outputs.package_path }} /tmp/${{ steps.find_package.outputs.package_name }}
./scripts/feeds update -a
./scripts/feeds install -a

- name: Build apk package
working-directory: ${{ matrix.sdk_name }}
run: |
rm -rf bin/packages
make defconfig
make package/moci/compile V=s

- name: Find and rename apk
id: find_apk
working-directory: ${{ matrix.sdk_name }}
run: |
APK_FILE=$(find bin/packages -name "moci*.apk" | head -n 1)
if [ -z "$APK_FILE" ]; then
echo "Error: apk package not found!"
exit 1
fi
VERSION="${{ needs.prepare.outputs.version }}"
NEW_NAME="moci_${VERSION}-r1_${{ matrix.arch }}.apk"
mv "$APK_FILE" "/tmp/$NEW_NAME"
echo "package_name=$NEW_NAME" >> $GITHUB_OUTPUT

- name: Upload to release
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: /tmp/${{ steps.find_package.outputs.package_name }}
files: /tmp/${{ steps.find_apk.outputs.package_name }}

- name: Upload artifact
if: github.event_name != 'release'
uses: actions/upload-artifact@v4
with:
name: moci-apk-${{ matrix.arch }}
path: /tmp/${{ steps.find_apk.outputs.package_name }}
15 changes: 12 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,14 +73,23 @@ scp -r moci/* root@192.168.1.1:/www/moci/

### Option 1: Package (Recommended)

Download the ipk for your architecture from [Releases](https://github.com/HudsonGraeme/MoCI/releases/latest):
Download the package for your architecture from [Releases](https://github.com/HudsonGraeme/MoCI/releases/latest).

**OpenWrt 24.10 and earlier (opkg):**

```bash
wget https://github.com/HudsonGraeme/MoCI/releases/latest/download/moci_VERSION_ARCH.ipk
opkg install moci_VERSION_ARCH.ipk
```

Available architectures: x86_64, ramips/mt7621, ath79, mediatek/filogic, bcm27xx, ipq40xx, mvebu, ipq806x
**OpenWrt 25.12+ (apk):**

```bash
wget https://github.com/HudsonGraeme/MoCI/releases/latest/download/moci_VERSION_ARCH.apk
apk add --allow-untrusted moci_VERSION_ARCH.apk
```

Available architectures: x86_64, mipsel_24kc, mips_24kc, aarch64_cortex-a53, aarch64_cortex-a72, arm_cortex-a7_neon-vfpv4, arm_cortex-a9_vfpv3-d16, arm_cortex-a15_neon-vfpv4

Replace `VERSION_ARCH` with your specific file from the releases page.

Expand Down Expand Up @@ -123,7 +132,7 @@ git clone https://github.com/HudsonGraeme/MoCI.git package/moci
make package/moci/compile
```

The package will be in `bin/packages/*/base/moci_*.ipk`
The package will be in `bin/packages/*/base/moci_*.ipk` (24.10) or `bin/packages/*/base/moci-*.apk` (25.12+)

---

Expand Down
6 changes: 6 additions & 0 deletions moci/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,12 @@ body {
transform: scale(0.98);
}

.action-btn-sm.danger:hover {
border-color: var(--alert-red);
color: var(--alert-red);
box-shadow: 0 0 8px rgba(255, 51, 51, 0.15);
}

.section-title {
font-family: var(--font-mono);
font-size: 12px;
Expand Down
Loading