Automated tools for updating the SANS NCL Team Answer Sheet from cyberskyline.com data.
Note: Built for a specific NCL team workflow. Documentation is functional but reflects organic development.
update_sheet.py- Main script for updating any category or all sheets./update_sheet.py osint # Preview OSINT ./update_sheet.py osint --yes # Update OSINT ./update_sheet.py all --yes # Update all sheets ./update_sheet.py cracking --test --yes # Test mode (rows 50+)
Available categories: osint, crypto, cracking, log, nta, forensics, scanning, web, enum, all
Individual category scripts still available for convenience:
update_osint_sheet.py,update_crypto_sheet.py, etc.update_all_sheets.py
These work the same as before but update_sheet.py is recommended.
cd ~/ncl-sheet-sync
# Preview single sheet
./update_sheet.py osint
# Preview all sheets
./update_sheet.py allTests on rows 50+ without touching existing data:
# Test single sheet
./update_sheet.py cracking --test --yes
# Test all sheets
./update_sheet.py all --test --yes# Update single sheet
./update_sheet.py osint --yes
# Update all sheets
./update_sheet.py all --yesEach script automatically:
- Safety Check: Before updating, checks if any work has been done (non-default dropdown values)
- If work detected: Aborts update to prevent data loss
- If safe: Clears old data and proceeds with update
- Fetches challenge data from cyberskyline.com
- Creates challenge marker rows (grey background)
- Populates question rows with:
- Question names
- Point values
- Answer Status dropdown with color coding:
- Yes = White text on green background
- No = White text on red background
- N/A = Black text on grey background (default)
- Needs Validation = Black text on beige/yellow background
- Ready to submit = Dark green text on light green background
- Team member tracking dropdowns with color coding:
- Nothing = White (invisible, default)
- Started = Light green
- Agree = Green, bold
- Disagree = Red background, white bold text
- Surrender = Black background, white bold text
- Applies borders and formatting
Essential:
- Logged into cyberskyline.com in a supported browser (Firefox, Chrome, Chromium, Brave, or Edge)
- Google Sheets OAuth2 credentials
- Python 3.8+ with required packages
Supported Platforms:
- Linux (tested on NixOS and Ubuntu)
- Windows 10/11
- macOS
The fastest way to get started:
# 1. Install Python dependencies (see "Installing Dependencies" below)
# 2. Run automated setup
python utils/auto_setup.py
# 3. Set up Google Sheets authentication
python utils/setup_google_auth.py
# 4. Edit config.py with your Sheet ID and World URL
# 5. Test it!
python update_sheet.py osintThat's it! The automated setup will detect your browser and extract cookies automatically.
If you have Nix installed, you get a reproducible environment with all dependencies:
cd ~/ncl-sheet-sync
nix-shell # Drops you into a shell with everything installedOr run scripts directly (they have nix-shell shebangs):
./update_sheet.py osintNote: Nix is optional but provides isolated, reproducible environments. To install Nix, visit https://nixos.org/download.html
Install dependencies with pip:
pip install gspread google-auth-oauthlib requests pycryptodomeWindows users: For Chrome cookie decryption, also install:
pip install pywin32On Debian/Ubuntu:
sudo apt install python3-gspread python3-requests python3-cryptoOn Fedora/RHEL:
sudo dnf install python3-gspread python3-requests python3-pycryptoOption A: Automated (Recommended)
python utils/auto_setup.pyThis will:
- Auto-detect your browser (Firefox, Chrome, Chromium, Brave, Edge)
- Extract cyberskyline.com cookies
- Create or update
config.pywith the cookie file path
Option B: Manual (if automated setup fails)
See manual instructions at the end of this section.
- Go to Google Cloud Console
- Create a new project (or select existing)
- Enable Google Sheets API:
- Go to "APIs & Services" > "Library"
- Search for "Google Sheets API"
- Click "Enable"
- Create OAuth 2.0 credentials:
- Go to "APIs & Services" > "Credentials"
- Click "Create Credentials" > "OAuth client ID"
- Application type: "Desktop app"
- Download the credentials JSON file as
credentials.json
- Place
credentials.jsonin the project directory - Run authentication script:
This will open a browser for OAuth authentication and create
python utils/setup_google_auth.py
token.pickle.
Edit config.py and update:
# Your Google Sheet ID (from the URL)
SHEET_ID = "your-sheet-id-here"
# Your NCL World URL
CYBERSKYLINE_URL = "https://cyberskyline.com/world/your-world-id"
# Token path (if setup_google_auth.py put it somewhere else)
TOKEN_PATH = "/path/to/token.pickle"Finding your Sheet ID: From your Google Sheet URL:
https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID_HERE/edit
Finding your World URL:
- Log into https://cyberskyline.com
- Navigate to your NCL Gymnasium or Team Game
- Copy the URL from your browser (format:
https://cyberskyline.com/world/...)
Note: Use Gymnasium for testing. Switch to Team Game URL when competition starts.
Test your configuration:
cd ~/ncl-sheet-sync
./update_sheet.py osint # Dry run to test authenticationIf you see challenge data, authentication is working!
Cyberskyline session cookies expire periodically. If you get authentication errors:
- Make sure you're logged into cyberskyline.com in your browser
- Re-run
python utils/auto_setup.pyto refresh cookies - The script will automatically update your config
Tip: Session cookies typically last several hours to days. You may need to refresh them before each competition day.
If auto_setup.py doesn't work for your setup, you can extract cookies manually:
# Linux/macOS
sqlite3 ~/.mozilla/firefox/*.default*/cookies.sqlite \
"SELECT name, value FROM moz_cookies WHERE host LIKE '%cyberskyline.com%'" \
| awk -F'|' '{printf "%s=%s; ", $1, $2}' > ~/cyberskyline_cookies.txt
# Windows (PowerShell)
# Find your profile first:
dir $env:APPDATA\Mozilla\Firefox\Profiles\
# Then extract (replace YOUR_PROFILE):
sqlite3 "$env:APPDATA\Mozilla\Firefox\Profiles\YOUR_PROFILE.default\cookies.sqlite" "SELECT name, value FROM moz_cookies WHERE host LIKE '%cyberskyline.com%'" > cookies.txtOr use the included helper script (Linux only):
python utils/extract_firefox_cookies.pyChrome cookies are encrypted on Windows. You'll need a tool like:
- EditThisCookie browser extension
- Export cookies for
cyberskyline.comin Netscape format - Convert to format:
name1=value1; name2=value2; ...
For any browser, you can use extensions like:
- EditThisCookie (Chrome/Edge)
- Cookie-Editor (Firefox/Chrome)
- Install extension
- Visit cyberskyline.com while logged in
- Click extension icon
- Export cookies for cyberskyline.com
- Format as:
cookie1=value1; cookie2=value2; ... - Save to
~/cyberskyline_cookies.txt - Update
COOKIE_FILEpath inconfig.py
The scripts include a safety check to prevent accidental data loss:
- Before updating: Checks Answer Status (column D) and team member columns (G-M) for non-default values
- If work detected: Aborts with an error message to prevent overwriting your work
- If safe: Clears rows 3-100 and regenerates from cyberskyline data
If you intentionally want to regenerate a sheet that has work in it:
- Manually clear the data range (rows 3-100) in Google Sheets
- Or manually reset dropdown values to defaults ("N/A" for Answer Status, "Nothing" for team members)
- Then run the update script again
IMPORTANT: Run the updater immediately when the competition opens, before anyone starts working!
When the NCL Team Game starts:
- Before the game opens: Make sure you're logged into cyberskyline.com in Firefox
- When challenges go live: Run the updater immediately:
cd ~/ncl-sheet-sync ./update_sheet.py all --yes
- All 9 category sheets will be populated with actual challenge names and points
- Team can now start working with the fully populated sheets
- Timing matters: Run the update BEFORE anyone changes any dropdowns from their defaults
- Data protection: If any sheets already have work in them (non-default dropdown values), those sheets will be skipped to prevent data loss
- Before competition: If you run the scripts early, you'll only get "Redacted" placeholders. Wait until the game goes live for actual challenge data.
- Cookie refresh: Make sure your cyberskyline cookies are fresh (re-extract if needed)
config.example.py- Template configuration file (commit to git)config.py- Your actual configuration (gitignored, create from example).gitignore- Protects sensitive files from being committedshell.nix- Nix development environment with all dependencies
update_sheet.py- Unified updater for single or all category sheetsupdate_sheet_template.py- Core logic library
auto_setup.py- Automated setup - detects browser, extracts cookies, creates config.py (cross-platform)setup_google_auth.py- Google OAuth authentication setupextract_firefox_cookies.py- Manual Firefox cookie extraction (Linux-only, use auto_setup.py instead)
update_all_sheets.py- Alternative script to update all sheetsupdate_*_sheet.py- Individual category updaters (9 files)
- Old development and diagnostic scripts (for reference only)