TreeMap
무료 오픈소스 디스크 사용량 시각화 도구로, 사각형 트리맵·중복 파일 탐색·디스크 가득 예측·정리 시 파일을 휴지통으로만 이동한다
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기메이커가 쓴 소개는 위의 한 줄이 전부입니다. 더 자세한 내용은 저장소 README에서 읽을 수 있습니다. 저장소 README ↗
README 전체를 불러오는 중입니다.
README 전문 보기 ↗Automatically syncs Canvas LMS assignments to Google Tasks and Canvas events to Google Calendar.
Canvas only offers a read-only calendar feed, so due dates never show up in the tools students actually use. Canvas2Google reads that feed and keeps a dedicated "Canvas" Google Calendar and a "Canvas" Google Tasks list up to date: creating new items, updating changed ones, and never making duplicates.
It runs as a web app (Flask on Vercel, Postgres on Neon, a daily scheduled sync) and as a command-line tool, both built on the same sync engine. I built it as a cybersecurity portfolio project, so the security design is as much the point as the features. See THREAT_MODEL.md.
Live at https://canvas2google.vercel.app. Sign-in is currently limited to an allowlist (single user) while the Google OAuth app awaits verification.
| Preview (dry run) | Settings |
|---|---|
Screenshots use fake demo data from scripts/demo_server.py.
flowchart LR
subgraph Vercel["Vercel (serverless, HTTPS)"]
W["Flask web app<br/>webapp/"]
T["/tasks/sync-all<br/>(cron secret)"]
end
U([Browser]) -->|session cookie + CSRF| W
C([Vercel Cron, daily]) -->|Bearer CRON_SECRET| T
W --> E["Sync engine<br/>canvas_sync/"]
T --> E
L([CLI: cli.py]) --> E
E -->|HTTPS, SSRF-guarded| F[(Canvas iCal feed)]
E -->|OAuth, least-privilege scopes| G[(Google Calendar + Tasks)]
W -->|TLS| D[(Neon Postgres<br/>secrets encrypted)]
canvas_sync/ Shared sync engine (no web code)
feed.py Fetch (HTTPS, size/timeout limits, no redirects) and parse the iCal feed
text.py HTML → plain text; strips script/style and terminal control characters
calendar_sync.py Events → Google Calendar (create / patch / re-link / dry run)
tasks_sync.py Assignments → Google Tasks
state.py Sync records + content fingerprints (versioned, migrated)
config.py Strict feed-URL validation (SSRF allowlist)
webapp/ Flask app: Google sign-in, encryption, database, pages, cron endpoint
cli.py Command-line front end
app.py Vercel entry point
tests/ Test suite (network blocked; fake Google services)
UID maps to its Google event or task ID in a per-user state record.PATCH.UPSERT works the same on SQLite and Postgres, across processes, and expires if a sync crashes.| Area | What's done |
|---|---|
| Least privilege | Google scopes are calendar.app.created (can only touch calendars this app created; verified by a 403 on others) and tasks. Partial consent is detected and refused. |
| Authentication | Google sign-in with state + PKCE, server-side code exchange, verified ID token, and an email allowlist checked before anything is stored. A fresh session starts on sign-in. |
| Secrets at rest | Feed URLs and Google tokens are encrypted with Fernet (AES + HMAC). The key lives outside the database, and each ciphertext is bound to its user row so it can't be moved to another account. |
| Web hardening | CSRF tokens; CSP with no scripts at all; __Host- Secure/HttpOnly/SameSite cookies; HSTS; no framing; no stack traces; auto-escaped templates. |
| SSRF | The server only fetches https://*.instructure.com/feeds/calendars/*.ics, never follows redirects, and rejects metadata IPs, credentials in URLs, odd ports and lookalike domains. |
| Untrusted input | Canvas content is treated as hostile: HTML is parsed, never rendered; control characters are stripped (no terminal escape injection); size and length caps apply. |
| Abuse controls | Database-backed rate limits (sign-in per IP, syncs and Canvas fetches per user, lockout after wrong cron secrets), with IPs stored only as keyed hashes. A user cap, and an invite-only or open sign-up mode. |
| Key rotation | APP_ENCRYPTION_KEY=NEW,OLD (MultiFernet): new data uses the newest key, the daily sync re-encrypts the rest, then the old key is removed. |
| Multi-user isolation | Tests prove one user can't see, change or delete another's data, and no route takes a user ID. |
| Secret hygiene | Nothing secret was ever committed (full-history scans before every push, using a scanner that is self-tested against a known fake). Errors never echo URLs or tokens. OAuth codes are redacted from request logs. |
| Supply chain | Every dependency pinned; Dependabot proposes updates weekly. CI pins GitHub Actions to exact commits and runs with read-only permissions. clean. clean, with three documented suppressions: one false positive, plus two deliberately fake values in the demo script. |
The full analysis (assets, trust boundaries, 30 threats, 14 remaining risks, and incidents caught during development) is in THREAT_MODEL.md.
pip install -r requirements.txt -r requirements-dev.txt
pytest --cov=canvas_sync --cov=webapp
PATCH semantics.state, non-allowlisted accounts, missing scopes), CSRF, encryption at rest, XSS escaping, SSRF inputs, the cron secret, HTTPS cookie flags, time zones across DST, and lock exclusivity across connections.GitHub Actions runs the tests, bandit and pip-audit on every push, using Python 3.12 like production (ci.yml).
Security checks:
pip-audit -r requirements.txt
bandit -r canvas_sync webapp cli.py app.py scripts
You'll need Python 3.12+, a Canvas calendar feed link, and a Google Cloud project with the Calendar and Tasks APIs enabled.
Demo with fake data (no accounts needed):
python scripts/demo_server.py # then open http://127.0.0.1:5050/demo-login
Command line:
python -m venv .venv && source .venv/bin/activate # Windows: .venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env # add CANVAS_FEED_URL; put a Desktop OAuth client in credentials.json
python cli.py # list upcoming items
python cli.py --dry-run # preview changes
python cli.py --sync-events --sync-tasks
Web app locally: fill in the web settings in .env (see .env.example), then:
flask --app webapp run # http://localhost:5000
flask --app "webapp:create_app(start_scheduler=True)" run # with automatic syncing
Deploying to Vercel: import the repo, connect a Neon Postgres database (Production only), and set the environment variables from .env.example plus SECURE_COOKIES=true, OAUTH_REDIRECT_URI and CRON_SECRET. vercel.json schedules the daily sync. The app refuses to start on Vercel without Postgres, rather than silently losing data.
A few things the project taught me, each written up in THREAT_MODEL.md §5:
Referrer-Policy: no-referrer would have broken every form in production, because the CSRF library checks the Referer header over HTTPS.python -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())"APP_ENCRYPTION_KEY=NEW_KEY,OLD_KEY and redeploy.flask --app webapp rotate-keys), everything is under the new key.APP_ENCRYPTION_KEY=NEW_KEY and redeploy.SIGNUP_MODE to open.Built by Noah (@channel-noah), a cybersecurity student, as a hands-on project in Python, APIs, OAuth and secure web development, working with an AI pair programmer (Claude Code). Not affiliated with Instructure or Google.
pip-auditbandit