feat: prepare repo for release with sanitized docs, license, pre-commit hook, and CI/CD workflows

This commit is contained in:
2026-08-23 14:47:05 -07:00
parent c48b1ebbd5
commit 3191f3239b
7 changed files with 374 additions and 34 deletions
+94
View File
@@ -0,0 +1,94 @@
#!/usr/bin/env bash
# ==============================================================================
# Git Pre-Commit Hook: Anti-Data-Leak & Secrets Protection
# ==============================================================================
# Prevents accidental commits of personal media, child data, student UUIDs,
# local paths, and credentials before code is committed or pushed.
# ==============================================================================
set -eo pipefail
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
NC='\033[0m'
ERRORS_FOUND=0
echo -e "${BOLD}🔍 Running pre-commit security & data leak checks...${NC}"
# ------------------------------------------------------------------------------
# 1. Check for forbidden staged files (Media, Archives, Real Exports)
# ------------------------------------------------------------------------------
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM)
if [ -n "$STAGED_FILES" ]; then
while IFS= read -r file; do
[ -z "$file" ] && continue
# Check for forbidden export data files & raw media
if [[ "$file" =~ (activities\.csv|activities\.json|exporter_job_info\.json|\.zip$|\.crx$|\.mp4$|\.mov$|\.webm$|\.heic$|\.jpg$|\.jpeg$) ]]; then
echo -e "${RED}❌ [DATA LEAK BLOCKED] Staged forbidden export/media file:${NC} $file"
ERRORS_FOUND=$((ERRORS_FOUND + 1))
fi
# Check for forbidden folder names
if [[ "$file" =~ ^(emmy|exports|data|test_data)/ ]]; then
echo -e "${RED}❌ [DATA LEAK BLOCKED] Staged file from sensitive directory:${NC} $file"
ERRORS_FOUND=$((ERRORS_FOUND + 1))
fi
done <<< "$STAGED_FILES"
fi
# ------------------------------------------------------------------------------
# 2. Check diff content for sensitive patterns in staged changes
# (Excludes the pre-commit hook file itself from self-matching)
# ------------------------------------------------------------------------------
STAGED_DIFF=$(git diff --cached -U0 -- ':!.githooks/*' ':!.git/*' | grep -E "^\+[^+]" || true)
if [ -n "$STAGED_DIFF" ]; then
# 2a. Check for local machine paths (e.g. /Users/<username> or /home/<username>)
LOCAL_PATH_MATCH=$(echo "$STAGED_DIFF" | grep -En "(/Users/|/home/)[a-zA-Z0-9_\.-]+" || true)
if [ -n "$LOCAL_PATH_MATCH" ]; then
echo -e "${RED}❌ [DATA LEAK BLOCKED] Detected local machine path (/Users/... or /home/...):${NC}"
echo "$LOCAL_PATH_MATCH" | head -n 3
ERRORS_FOUND=$((ERRORS_FOUND + 1))
fi
# 2b. Check for student UUIDs (e.g. 8-4-4-4-12 hex format)
UUID_MATCH=$(echo "$STAGED_DIFF" | grep -En "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}" || true)
if [ -n "$UUID_MATCH" ]; then
echo -e "${RED}❌ [DATA LEAK BLOCKED] Detected hardcoded student/record UUID in diff:${NC}"
echo "$UUID_MATCH" | head -n 3
ERRORS_FOUND=$((ERRORS_FOUND + 1))
fi
# 2c. Check for AWS / Token credentials
AWS_KEY_MATCH=$(echo "$STAGED_DIFF" | grep -En "AKIA[0-9A-Z]{16}" || true)
if [ -n "$AWS_KEY_MATCH" ]; then
echo -e "${RED}❌ [SECURITY LEAK BLOCKED] Detected potential AWS Access Key:${NC}"
echo "$AWS_KEY_MATCH" | head -n 3
ERRORS_FOUND=$((ERRORS_FOUND + 1))
fi
SESSION_TOKEN_MATCH=$(echo "$STAGED_DIFF" | grep -En "(_brightwheel_session=|Bearer[[:space:]]+[A-Za-z0-9_\-\.]{20,})" || true)
if [ -n "$SESSION_TOKEN_MATCH" ]; then
echo -e "${RED}❌ [SECURITY LEAK BLOCKED] Detected session token or bearer credential:${NC}"
echo "$SESSION_TOKEN_MATCH" | head -n 3
ERRORS_FOUND=$((ERRORS_FOUND + 1))
fi
fi
# ------------------------------------------------------------------------------
# 3. Verdict
# ------------------------------------------------------------------------------
if [ "$ERRORS_FOUND" -gt 0 ]; then
echo -e "\n${RED}${BOLD}🚨 COMMIT REJECTED:${NC} $ERRORS_FOUND security / data leak issue(s) detected."
echo -e "${YELLOW}Please remove sensitive files/tokens before committing.${NC}"
echo -e "${YELLOW}(If this is an intentional false positive, bypass with 'git commit --no-verify')${NC}\n"
exit 1
fi
echo -e "${GREEN}✅ Pre-commit leak check passed! No sensitive data detected.${NC}"
exit 0
+108
View File
@@ -0,0 +1,108 @@
name: CI Verification
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
verify:
name: Lint & Extension Integrity Check
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Validate manifest.json
run: |
echo "🔍 Validating manifest.json syntax and schema..."
node -e "
const manifest = JSON.parse(require('fs').readFileSync('manifest.json', 'utf8'));
if (manifest.manifest_version !== 3) {
console.error('Error: Manifest version must be 3');
process.exit(1);
}
console.log('✅ manifest.json is valid (v' + manifest.version + ')');
"
- name: Verify Referenced Assets & Files
run: |
echo "🔍 Checking that all files referenced in manifest.json exist..."
node -e "
const fs = require('fs');
const manifest = JSON.parse(fs.readFileSync('manifest.json', 'utf8'));
const files = [
manifest.background?.service_worker,
manifest.action?.default_popup,
...(manifest.content_scripts?.flatMap(cs => cs.js) || []),
...Object.values(manifest.icons || {})
].filter(Boolean);
let missing = 0;
for (const file of files) {
if (!fs.existsSync(file)) {
console.error('❌ Missing file referenced in manifest:', file);
missing++;
} else {
console.log(' ✓ Found:', file);
}
}
if (missing > 0) process.exit(1);
console.log('✅ All manifest files exist!');
"
- name: JavaScript Syntax Check
run: |
echo "🔍 Checking JavaScript syntax..."
find scripts dashboard popup -name "*.js" | while read -r jsfile; do
node --check "$jsfile"
echo " ✓ Syntax valid: $jsfile"
done
echo "✅ All JavaScript files passed syntax verification!"
- name: Run Anti-Data-Leak Audit
run: |
echo "🔍 Checking repository for sensitive paths and keys..."
ERRORS=0
# Check for forbidden export data or test archives
if find . -name "*.zip" -o -name "activities.csv" -o -name "activities.json" | grep -v ".git" | grep -q .; then
echo "❌ Forbidden data/archive file found in repository!"
ERRORS=$((ERRORS + 1))
fi
# Check for hardcoded local machine paths
if grep -rEn "(/Users/|/home/)[a-zA-Z0-9_\.-]+" --exclude-dir=".git" --exclude-dir=".githooks" --exclude="*.log" .; then
echo "❌ Hardcoded local path detected in source code!"
ERRORS=$((ERRORS + 1))
fi
if [ "$ERRORS" -gt 0 ]; then
echo "🚨 CI Audit failed with $ERRORS issue(s)."
exit 1
fi
echo "✅ Security & data leak check passed!"
- name: Test Package Build
run: |
echo "📦 Building test extension package..."
zip -r brightwheel-exporter-test.zip . \
-x "*.git*" \
-x ".githooks/*" \
-x ".github/*" \
-x "emmy/*" \
-x "exports/*" \
-x "data/*" \
-x "*.md" \
-x "LICENSE" \
-x "brightwheel-exporter-test.zip"
echo "✅ Package built successfully:"
unzip -l brightwheel-exporter-test.zip
+71
View File
@@ -0,0 +1,71 @@
name: Build & Package Extension Release
on:
release:
types: [published]
workflow_dispatch:
jobs:
build-release:
name: Build ZIP & Attach to GitHub Release
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v4
- name: Read Extension Version
id: version
run: |
VERSION=$(node -p "require('./manifest.json').version")
echo "EXT_VERSION=$VERSION" >> $GITHUB_ENV
echo "Building extension release for version: $VERSION"
- name: Build Production Extension ZIP
run: |
ZIP_NAME="brightwheel-exporter-v${{ env.EXT_VERSION }}.zip"
echo "📦 Packaging $ZIP_NAME..."
zip -r "$ZIP_NAME" . \
-x "*.git*" \
-x ".githooks/*" \
-x ".github/*" \
-x "emmy/*" \
-x "exports/*" \
-x "data/*" \
-x "test_data/*" \
-x "*.md" \
-x "LICENSE" \
-x "*.zip"
echo "ZIP_FILE=$ZIP_NAME" >> $GITHUB_ENV
echo "✅ Package contents verified:"
unzip -l "$ZIP_NAME"
- name: Upload ZIP to GitHub Release Assets
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: ${{ env.ZIP_FILE }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# ------------------------------------------------------------------------
# TODO: Auto-Publish to Chrome Web Store
# ------------------------------------------------------------------------
# Once you create the initial extension item in the Chrome Web Store Developer
# Dashboard and generate Google Cloud OAuth credentials, uncomment this step
# and configure the following 4 secrets in GitHub (Settings > Secrets > Actions):
# - CHROME_EXTENSION_ID
# - CHROME_CLIENT_ID
# - CHROME_CLIENT_SECRET
# - CHROME_REFRESH_TOKEN
# ------------------------------------------------------------------------
# - name: Upload & Publish to Chrome Web Store
# uses: fregante/chrome-web-store-upload-action@v2
# with:
# file-name: ${{ env.ZIP_FILE }}
# extension-id: ${{ secrets.CHROME_EXTENSION_ID }}
# client-id: ${{ secrets.CHROME_CLIENT_ID }}
# client-secret: ${{ secrets.CHROME_CLIENT_SECRET }}
# refresh-token: ${{ secrets.CHROME_REFRESH_TOKEN }}
# publish: true
+23 -1
View File
@@ -1 +1,23 @@
emmy/ # OS and system files
.DS_Store
Thumbs.db
*.tmp
# User Export Data & Backups (Prevent leaking child photos/videos/records)
emmy/
exports/
data/
test_data/
*.zip
*.crx
# IDE and Editor files
.vscode/
.idea/
*.swp
*.swo
# Node / Package Manager logs (if applicable)
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 CoderSherlock
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+56 -31
View File
@@ -1,58 +1,70 @@
# Brightwheel Memories & Reports Exporter Chrome Extension # 🌟 Brightwheel Exporter (Chrome Extension)
A Chrome Extension (Manifest V3) that allows parents and guardians to bulk export all of their children's memories, high-resolution photos, videos, daily care reports (meals, naps, potty), learning milestones, and activity manifests from Brightwheel into an organized offline ZIP archive. [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
[![Manifest V3](https://img.shields.io/badge/Manifest-V3-success.svg)](manifest.json)
[![Platform](https://img.shields.io/badge/Platform-Chrome%20Extension-orange.svg)](#installation)
> **Save and back up all your child's precious preschool & daycare memories, high-resolution photos, HD videos, daily care logs, and developmental milestones from Brightwheel.**
---
## ✨ Features ## ✨ Features
- **Full Data Manifest**: Exports complete student records, activities, and teacher notes in structured `manifest.json` format. - 📸 **High-Resolution Media Archiver**: Downloads original, full-resolution photos and HD videos organized neatly into date folders (`/photos/YYYY-MM/` and `/videos/YYYY-MM/`).
- **High-Resolution Media**: Downloads original full-size photos and videos organized neatly by year and month (`/YYYY/MM/photos/` & `.../videos/`). - 📂 **Direct Local Folder Streaming**: Streams files directly to your chosen local folder via the **File System Access API**—no browser memory crashes or ZIP size limits!
- **Daily Care Logs**: Captures meal entries, nap start/end times and durations, diaper/potty logs, and medication logs. - 📦 **Instant ZIP Archive Export**: One-click bundle creation using zero-compression `STORE` packaging for lightning-fast downloads.
- **Learning Milestones**: Backs up developmental milestones, curriculum tags, and teacher observations. - 🗓️ **Complete Daily Care Records**: Captures meals, exact nap start/end times and calculated durations, diaper & potty logs, check-in/out records, and medication logs.
- **Attendance & Communication**: Includes check-in/out records, health screenings, and message history. - 🎯 **Milestones & Learning Observations**: Backs up developmental milestones, curriculum tags, and teacher notes.
- **Spreadsheet Ready**: Generates an `activities.csv` spreadsheet compatible with Microsoft Excel, Numbers, and Google Sheets. - 🌐 **Interactive Offline Timeline Viewer**: Bundles a standalone, elegant `index.html` viewer inside your export so you can search captions, filter events, and relive memories locally with no internet required.
- **Offline Interactive Timeline Viewer**: Bundles a standalone `index.html` inside the exported ZIP so you can browse your child's photos, search captions, and filter events locally on your computer with no internet required. - 📊 **Spreadsheet & Developer Friendly**: Generates a clean `activities.csv` (compatible with Excel, Numbers, and Google Sheets) as well as unified `activities.json` / `manifest.json` files.
- **Safe & Private**: Runs 100% locally in your browser. Seamlessly uses your active Brightwheel logged-in session cookies without asking for passwords or transmitting your family's data to third parties. - 🛡️ **100% Private & Client-Side**: Operates entirely in your browser using your active logged-in Brightwheel session. No passwords, credentials, or child data are ever transmitted to any third-party servers.
- **Smart Rate-Limiting**: Includes automatic exponential backoff (handling HTTP 429 throttling) and configurable pacing delays. - ⚡ **Smart Throttling & Rate-Limiting**: Built-in exponential backoff automatically handles API pacing and prevents HTTP 429 rate limit errors.
--- ---
## 🚀 Installation (Load Unpacked in Chrome) ## 🚀 Installation (Load Unpacked in Chrome)
1. Open Google Chrome and navigate to `chrome://extensions/`. 1. **Clone or download this repository:**
2. Turn **ON** the **Developer mode** toggle in the top-right corner. ```bash
3. Click the **Load unpacked** button in the top-left corner. git clone https://github.com/CoderSherlock/brightwheel-exporter.git
4. Select this directory:
``` ```
/Users/haopengzhan/Documents/MyGit/brightwheel-exporter-chrome-extension 2. Open Google Chrome and navigate to:
``` ```
5. The **Brightwheel Memories & Reports Exporter** icon will now appear in your Chrome toolbar! chrome://extensions/
```
3. Enable **Developer mode** using the toggle switch in the top-right corner.
4. Click the **Load unpacked** button in the top-left corner.
5. Select the cloned `brightwheel-exporter` directory.
6. The **Brightwheel Exporter** icon will now appear in your browser toolbar!
--- ---
## 📖 How to Use ## 📖 How to Use
1. Log into your Brightwheel account in Chrome at [https://schools.mybrightwheel.com](https://schools.mybrightwheel.com). 1. Log into your Brightwheel account in Chrome at [https://schools.mybrightwheel.com](https://schools.mybrightwheel.com).
2. Navigate to your child's feed page (e.g. `https://schools.mybrightwheel.com/students/edda433b-2a24-4da7-bbd5-9f131b4b2565/feed`). 2. Navigate to your child's page (or open any Brightwheel feed).
3. Click the **Brightwheel Exporter** icon in your browser toolbar (or click the floating **"Export Brightwheel Data"** button on the bottom right of the page). 3. Click the **Brightwheel Exporter** extension icon in your Chrome toolbar (or click the floating **"Export Brightwheel Data"** button on the bottom right of the page).
4. Click **Open Exporter Dashboard**. 4. Click **Open Exporter Dashboard**.
5. In the Dashboard: 5. In the Dashboard:
- Select your child (or enter a custom student UUID). - Select your child from the dropdown (or enter a custom student UUID).
- Choose your date range (**All Time**, **This Year**, or **Custom**). - Choose your date range (**All Time**, **This Year**, or **Custom Date Range**).
- Select the content categories and export formats you want. - Choose your save mode:
- **Save Directly to Local Folder** *(Recommended for large feeds)*
- **Download as ZIP Archive**
- Select the content categories and export formats you wish to include.
- Click **Start Complete Export**. - Click **Start Complete Export**.
6. The extension will automatically harvest activities, download media with live progress tracking, and generate your downloadable ZIP archive! 6. Monitor the real-time progress bar and log window as your files and media are safely saved to your computer.
--- ---
## 📁 Exported Archive Structure ## 📁 Exported Folder & Archive Structure
Inside the downloaded `.zip` file, you will find:
``` ```
[ChildName]_[YYYY-MM-DD]/ [ChildName]_[YYYY-MM-DD]/
├── index.html # Offline interactive timeline viewer ├── index.html # Interactive offline timeline viewer
├── manifest.json # Complete raw structured data ├── activities.csv # Spreadsheet for Excel / Google Sheets / Numbers
├── activities.csv # Spreadsheet for Excel / Google Sheets ├── activities.json # Complete unified structured data records
├── exporter_job_info.json # Metadata summary & statistics of the export
├── photos/ ├── photos/
│ ├── 2026-08/ │ ├── 2026-08/
│ │ ├── 2026-08-20_abc123.jpg │ │ ├── 2026-08-20_abc123.jpg
@@ -67,5 +79,18 @@ Inside the downloaded `.zip` file, you will find:
## 🔒 Privacy & Security ## 🔒 Privacy & Security
- **Zero Data Collection**: This extension does not send any personal data, photos, credentials, or cookies to external servers. All requests happen strictly between your browser and Brightwheel/AWS S3. - **No Remote Servers**: This extension is 100% open source and runs entirely on your local machine.
- **Local Packaging**: The ZIP compression and HTML viewer creation happen entirely in your local browser memory using client-side JSZip. - **Session Authentication**: Uses your existing Brightwheel browser cookies securely. No logins or API keys are ever stored or shared.
- **Direct S3/CDN Downloads**: Media files are downloaded directly from Brightwheel's official AWS S3 buckets and CloudFront distribution endpoints straight to your disk.
---
## ⚠️ Disclaimer
This project is an independent, community-driven open-source tool. It is **not affiliated with, endorsed by, sponsored by, or associated with Brightwheel** or its parent companies. "Brightwheel" is a registered trademark of its respective owner.
---
## 📄 License
Distributed under the [MIT License](LICENSE). See `LICENSE` for more information.
+1 -2
View File
@@ -38,7 +38,6 @@
"https://*.brightwheel.com/*", "https://*.brightwheel.com/*",
"https://*.cloudfront.net/*", "https://*.cloudfront.net/*",
"https://*.amazonaws.com/*", "https://*.amazonaws.com/*",
"https://*.s3.amazonaws.com/*", "https://*.s3.amazonaws.com/*"
"<all_urls>"
] ]
} }