Add legal disclaimers, /terms page, and GPL compliance NOTICE - #125
Add legal disclaimers, /terms page, and GPL compliance NOTICE#125tomkabel wants to merge 9 commits into
Conversation
- Create /web/terms route in web.py with full ToS page - Add terms.html template with: - Prominent legal warning about user responsibility - Permitted/prohibited uses sections - DMCA notice and contact info - Legal framework references (Cox v Sony, Yout v RIAA) - Update README.md with: - Legitimate use cases section - Legal disclaimer about copyright/DMCA compliance - Link to full Terms of Service - Fix base.html footer links to use /web/terms - Add NOTICE file for GPL compliance with third-party attributions - Add DMCA §1201 notice emphasizing substantial non-infringing uses Per legal-analysis-report.md Priority 1 actions.
- README.md: fix /terms link to /web/terms - base.html: remove broken /privacy link from footer - web.py: remove CSRF token from public GET-only terms page - terms.html: add governing law (Estonia), DMCA agent section, GPL §§15-16 reference, change notification mechanism, proper consent statement, remove case law citations - NOTICE: add GPL §§15-16 warranty disclaimer section, fix all copyright years to 2023-2026, remove duplicate DMCA notice, reference Terms of Service instead - terms.html: reorder Legal Framework grid to reference GPL license instead of pending cases
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📝 WalkthroughWalkthroughAdds licensing/legal documentation and a Terms of Service web page: a top-level Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Server
participant TemplateEngine
participant CookieStorage
Client->>Server: GET /web/terms
Server->>CookieStorage: read csrf_token cookie
alt csrf_token exists
Server->>Server: reuse existing token
else no csrf_token
Server->>Server: generate csrf_token
end
Server->>TemplateEngine: render `terms.html` with last_updated and csrf_token
TemplateEngine-->>Server: rendered HTML
Server->>CookieStorage: set csrf_token cookie (if new)
Server-->>Client: 200 OK + HTML (cookie set/updated)
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
Note Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it. Generating unit tests... This may take up to 20 minutes. |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/templates/terms.html`:
- Around line 20-22: The template currently shows a misleading auto-generated
date via {{ current_year }}; update the terms.html template to render a
dedicated last_updated variable (e.g., {{ last_updated }}) instead of
current_year, and ensure the route that renders the template passes an explicit
last_updated value into get_template_context (or hardcode the real revision date
in the template); look for uses of get_template_context and the current_year
variable in terms.html and replace with last_updated, and update the terms page
handler to supply a proper string like "April 26, 2026".
- Around line 187-195: The templates reference an invalid link href="/privacy"
(anchor element in terms.html and base.html); remove that anchor (or its parent
container entry) from both templates until a /web-scoped privacy route and
template are implemented, ensuring you specifically update the <a
href="/privacy"...> element in terms.html and the matching <a
href="/privacy"...> in base.html to avoid broken 404 links.
In `@NOTICE`:
- Around line 49-52: Update the NOTICE entries as follows: change the HTMX
license from "Apache 2.0" to "0BSD" and update its URL to
"https://github.com/bigskysoftware/htmx" (locate the HTMX block labeled "HTMX");
update the SQLAlchemy copyright range from "(c) 2005-2023" to "(c) 2007-2026"
(locate the SQLAlchemy entry); and remove the copyright line for "yt-dlp"
entirely since it is released under The Unlicense/public domain (locate the
yt-dlp entry and delete its copyright line).
In `@README.md`:
- Line 68: The README link points to /terms which 404s because the router is
mounted with prefix="/web" and the handler is declared as `@router.get`("/terms"),
making the correct public path /web/terms; update the README link to /web/terms
to match the route (and mirror the existing footer change in the base template).
🪄 Autofix (Beta)
✅ Autofix completed
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f09c6da7-a5b4-4723-90f4-9f15bdf112ef
📒 Files selected for processing (5)
NOTICEREADME.mdapp/api/routes/web.pyapp/templates/base.htmlapp/templates/terms.html
|
✅ Unit tests committed locally. Commit: |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
tests/test_api/test_web_routes.py (1)
1930-1950: Test name implies "new token each request" but assertions don't verify that.
test_terms_page_new_csrf_token_each_requestonly asserts both tokens are non-Noneand the inline comment explicitly says values "may or may not differ." The name is misleading vs. behavior. Either rename to reflect what's actually tested (e.g.,test_terms_page_sets_csrf_cookie_on_each_fresh_client) or strengthen the assertion to compare tokens deliberately.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_api/test_web_routes.py` around lines 1930 - 1950, The test test_terms_page_new_csrf_token_each_request currently only checks that csrf1 and csrf2 are non-None but the name implies they should differ; to fix, update the assertions in that function to assert that csrf1 != csrf2 (after obtaining csrf1 and csrf2 from response1.cookies.get("csrf_token") and response2.cookies.get("csrf_token")) so the test verifies a new CSRF token is set for each independent request; alternatively, if you prefer to keep the existing behavior, rename the test to test_terms_page_sets_csrf_cookie_on_each_fresh_client to match the current assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/test_api/test_web_routes.py`:
- Around line 1844-1857: The test test_terms_page_contains_current_year uses a
loose substring check and UTC timestamp which risks flakiness; update it to
compute current_year with the same timezone the app uses (use
datetime.now().year) and assert a stronger pattern against response.text —
either assert f"© {current_year}" is contained or use
re.search(rf"\b{current_year}\b", response.text) to match the year as a whole
word; modify the assertion that references current_year and import re if using
the regex approach.
- Around line 1952-1969: The test test_terms_page_existing_csrf_cookie_reused is
incorrectly asserting on response.cookies (which only reflects Set-Cookie
headers) — change the assertion to verify reuse by reading the client's cookie
jar or the rendered HTML instead: after the second request, get the token via
client.cookies.get("csrf_token") (or parse second.text for the embedded CSRF
value if your template inserts it) and assert that equals existing_token; update
the test to use client.cookies.get("csrf_token") or HTML parsing rather than
second.cookies.get("csrf_token").
---
Nitpick comments:
In `@tests/test_api/test_web_routes.py`:
- Around line 1930-1950: The test test_terms_page_new_csrf_token_each_request
currently only checks that csrf1 and csrf2 are non-None but the name implies
they should differ; to fix, update the assertions in that function to assert
that csrf1 != csrf2 (after obtaining csrf1 and csrf2 from
response1.cookies.get("csrf_token") and response2.cookies.get("csrf_token")) so
the test verifies a new CSRF token is set for each independent request;
alternatively, if you prefer to keep the existing behavior, rename the test to
test_terms_page_sets_csrf_cookie_on_each_fresh_client to match the current
assertions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4c5f45ce-ab92-451a-88a7-bd0e58a08e61
📒 Files selected for processing (1)
tests/test_api/test_web_routes.py
| async def test_terms_page_contains_current_year(self): | ||
| """Terms page should render current_year from template context.""" | ||
| from datetime import UTC, datetime | ||
|
|
||
| current_year = str(datetime.now(UTC).year) | ||
|
|
||
| async with AsyncClient( | ||
| transport=ASGITransport(app=app), | ||
| base_url="http://test", | ||
| follow_redirects=False, | ||
| ) as client: | ||
| response = await client.get("/web/terms") | ||
|
|
||
| assert current_year in response.text |
There was a problem hiding this comment.
Year-boundary flake risk and weak substring match.
current_year is a 4-digit number that can incidentally appear in unrelated content (e.g., a phone number, statute reference, or copyright span like 2024-2026). Computing it via datetime.now(UTC).year also opens a tiny window for flakiness across a year boundary if the template uses local time vs. UTC. Consider asserting against an explicit copyright pattern instead, e.g. f"© {current_year}" or re.search(rf"\b{current_year}\b", response.text).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/test_api/test_web_routes.py` around lines 1844 - 1857, The test
test_terms_page_contains_current_year uses a loose substring check and UTC
timestamp which risks flakiness; update it to compute current_year with the same
timezone the app uses (use datetime.now().year) and assert a stronger pattern
against response.text — either assert f"© {current_year}" is contained or use
re.search(rf"\b{current_year}\b", response.text) to match the year as a whole
word; modify the assertion that references current_year and import re if using
the regex approach.
| async def test_terms_page_existing_csrf_cookie_reused(self): | ||
| """When a csrf_token cookie is sent with the request, the same token is reused.""" | ||
| async with AsyncClient( | ||
| transport=ASGITransport(app=app), | ||
| base_url="http://test", | ||
| follow_redirects=False, | ||
| ) as client: | ||
| # First request: obtain a fresh CSRF token | ||
| first = await client.get("/web/terms") | ||
| existing_token = first.cookies.get("csrf_token") | ||
| assert existing_token is not None | ||
|
|
||
| # Second request within the same client so the cookie jar carries the token | ||
| second = await client.get("/web/terms") | ||
|
|
||
| returned_token = second.cookies.get("csrf_token") | ||
| # The server should honour the pre-existing cookie and return the same value | ||
| assert returned_token == existing_token |
There was a problem hiding this comment.
response.cookies only reflects Set-Cookie headers, not jar state — assertion likely fails when the server reuses an existing cookie.
In httpx, response.cookies is populated from the response's Set-Cookie headers, not from the client's cookie jar. If the server's /web/terms handler honors the incoming csrf_token cookie and does not re-set it (which is what "reused" implies), then second.cookies.get("csrf_token") will be None and the equality assertion will fail. Conversely, if the server always sets the cookie, the test name is misleading.
To actually validate "reuse," inspect the rendered HTML (where the token is embedded) or client.cookies instead:
🛠 Proposed fix
- second = await client.get("/web/terms")
-
- returned_token = second.cookies.get("csrf_token")
- # The server should honour the pre-existing cookie and return the same value
- assert returned_token == existing_token
+ second = await client.get("/web/terms")
+ # Use the client's cookie jar (server may not re-set the cookie if reused).
+ jar_token = client.cookies.get("csrf_token")
+
+ assert jar_token == existing_token
+ # And the rendered page should embed the same token.
+ assert existing_token in second.text🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/test_api/test_web_routes.py` around lines 1952 - 1969, The test
test_terms_page_existing_csrf_cookie_reused is incorrectly asserting on
response.cookies (which only reflects Set-Cookie headers) — change the assertion
to verify reuse by reading the client's cookie jar or the rendered HTML instead:
after the second request, get the token via client.cookies.get("csrf_token") (or
parse second.text for the embedded CSRF value if your template inserts it) and
assert that equals existing_token; update the test to use
client.cookies.get("csrf_token") or HTML parsing rather than
second.cookies.get("csrf_token").
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. Fixes Applied SuccessfullyFixed 5 file(s) based on 4 unresolved review comments. Files modified:
Commit: The changes have been pushed to the Time taken: |
Fixed 5 file(s) based on 4 unresolved review comments. Co-authored-by: CodeRabbit <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
app/templates/terms.html (1)
20-22:last_updatedcontext value is unused — date is hardcoded twice.The route passes
last_updated="April 26, 2026"intoget_template_context(web.pyline 426), but this template renders the same string as a literal instead of{{ last_updated }}. Result: the route argument is dead code, and any future date change requires editing both files — easy to miss for a legal page.Either drop the kwarg from the route, or render the variable here so there's a single source of truth.
♻️ Proposed fix
- Last updated: April 26, 2026 + Last updated: {{ last_updated }}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/templates/terms.html` around lines 20 - 22, The template currently hardcodes the date string instead of using the context variable, so update the terms.html paragraph to render the passed-in context value (use the last_updated template variable) instead of the literal "April 26, 2026"; locate the paragraph in terms.html and replace the literal date with the template expression for last_updated (or alternatively remove the last_updated kwarg passed into get_template_context in web.py if you prefer to drop the dead argument) so there is a single source of truth.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/templates/terms.html`:
- Around line 31-33: The SVG path in the <svg class="h-6 w-6 text-amber-400">
element contains extra, malformed path commands after the intended
warning-triangle path; locate the <path ... d="..."> inside that SVG and replace
its d attribute with the canonical Heroicons exclamation-triangle outline path
(ending with ...zM12 15.75h.008v.008H12v-.008z) so the icon renders correctly
without the stray strokes.
---
Nitpick comments:
In `@app/templates/terms.html`:
- Around line 20-22: The template currently hardcodes the date string instead of
using the context variable, so update the terms.html paragraph to render the
passed-in context value (use the last_updated template variable) instead of the
literal "April 26, 2026"; locate the paragraph in terms.html and replace the
literal date with the template expression for last_updated (or alternatively
remove the last_updated kwarg passed into get_template_context in web.py if you
prefer to drop the dead argument) so there is a single source of truth.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 8418f9a2-b016-4ac8-bfbe-868253932322
📒 Files selected for processing (5)
NOTICEREADME.mdapp/api/routes/web.pyapp/templates/base.htmlapp/templates/terms.html
✅ Files skipped from review due to trivial changes (2)
- NOTICE
- README.md
🚧 Files skipped from review as they are similar to previous changes (1)
- app/templates/base.html
Signed-off-by: tomkabel <[email protected]>
…claimer
Resolved conflicts:
- NOTICE: kept 2023-2026 copyright range (consistent with other entries)
- app/api/routes/web.py: removed undefined `csrf_token=token` from terms_page
- app/templates/terms.html: kept template variable `{{ last_updated }}`, removed dead gap-6
…-vooglaadija into feature/legal-disclaimer
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/templates/terms.html`:
- Around line 125-130: Replace the vague DMCA contact paragraph inside the "DMCA
Designated Agent" block in app/templates/terms.html with statutory-compliant
contact information: include the designated agent's full name/title, physical
mailing address, telephone number, and a specific email address or link to a
takedown form; if this is a template, swap the current sentence for an explicit
placeholder string that lists the required fields (e.g. "DESIGNATED AGENT NAME,
ADDRESS, PHONE, EMAIL / takedown form URL — replace before production") so it's
clear the values must be filled in before deployment.
- Line 201: The displayed label in the span that currently reads 'EU: DSA Art.
17, Copyright Directive' is factually incorrect; update the text in the template
(the span containing "EU:" in app/templates/terms.html) to separately reference
the Digital Services Act and the Copyright Directive and, if desired, include
precise identifiers—e.g. "EU: Digital Services Act (Regulation EU 2022/2065);
Copyright Directive (Directive (EU) 2019/790, Art. 17)"—so the DSA and Article
17 of the Copyright Directive are not conflated.
🪄 Autofix (Beta)
❌ Autofix failed (check again to retry)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 4f0279fc-5f6d-40ae-8893-ddd948e70114
📒 Files selected for processing (3)
NOTICEapp/api/routes/web.pyapp/templates/terms.html
✅ Files skipped from review due to trivial changes (2)
- app/api/routes/web.py
- NOTICE
| <div class="bg-surface-800/50 rounded-lg p-4 mt-4"> | ||
| <h4 class="text-sm font-display font-semibold text-gray-300 mb-2">DMCA Designated Agent</h4> | ||
| <p class="text-xs text-gray-500 font-mono"> | ||
| To file a DMCA takedown request, contact the service administrator at the email address associated with this service. | ||
| </p> | ||
| </div> |
There was a problem hiding this comment.
DMCA designated agent contact information is too vague.
DMCA safe harbor provisions under 17 U.S.C. § 512(c)(2) require specific contact information (name, address, telephone, email) for the designated agent. "The email address associated with this service" doesn't meet statutory requirements.
🛡️ Suggested improvement
Consider either:
- Adding a specific contact email/form if this is a production deployment
- Adding a placeholder that makes it clear this needs to be filled in:
<h4 class="text-sm font-display font-semibold text-gray-300 mb-2">DMCA Designated Agent</h4>
- <p class="text-xs text-gray-500 font-mono">
- To file a DMCA takedown request, contact the service administrator at the email address associated with this service.
- </p>
+ <p class="text-xs text-gray-500">
+ <strong class="text-amber-400">[TODO: Add specific agent name and contact details per 17 U.S.C. § 512(c)(2)]</strong><br>
+ Email: <span class="font-mono">[[email protected]]</span>
+ </p>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/templates/terms.html` around lines 125 - 130, Replace the vague DMCA
contact paragraph inside the "DMCA Designated Agent" block in
app/templates/terms.html with statutory-compliant contact information: include
the designated agent's full name/title, physical mailing address, telephone
number, and a specific email address or link to a takedown form; if this is a
template, swap the current sentence for an explicit placeholder string that
lists the required fields (e.g. "DESIGNATED AGENT NAME, ADDRESS, PHONE, EMAIL /
takedown form URL — replace before production") so it's clear the values must be
filled in before deployment.
|
Note Autofix is a beta feature. Expect some limitations and changes as we gather feedback and continue to improve it. The branch was updated while autofix was in progress. Please try again. |
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Summary by CodeRabbit
New Features
Documentation
Tests