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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ Release tags are `vYYYY.MM.DD[.n]`. Installers and the Claude Code plugin consum

## Unreleased

- grcskills.com/report/: how to report illegal content or a copyright issue (EU Digital Services Act Art. 16 notices, Art. 12 point of contact), with a prefilled email and a private GitHub advisory as the two channels. Linked from the footer, listed in the sitemap, mentioned in the README; `report` is a reserved author slug.

## v2026.10.20.3

- ISMS Copilot, the GRC specialist your AI calls, gets its own place: a home band after "What comes back" with the connect URL and a copy button, a "Connect your AI" menu link, a clearer note under "Run with ISMS Copilot" on every skill page, and an FAQ answer on how to connect. Each link carries its own `ref=grcskills-*`. The heyGRC bar, home band and cards are unchanged (#14).
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ assets/ README banner, social preview, demo run

Authors can ask for a correction to their profile or skill, or for removal, by opening an issue (or a private security advisory if it is sensitive). Maintainers act on it promptly; a removed author's profile and skills leave the next release.

To report illegal content or a copyright issue (EU Digital Services Act notices included), see [grcskills.com/report](https://grcskills.com/report/): it lists what to include and how to send it privately.

## Credits and notices

By [ISMS Copilot](https://www.ismscopilot.com/mcp?ref=grcskills-readme), maintained by Better ISMS. MIT licensed ([LICENSE](LICENSE)); each skill credits its author.
Expand Down
1 change: 1 addition & 0 deletions site/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export default function RootLayout({ children }: { children: React.ReactNode })
<Link href="/skills/">Skills</Link>
<Link href="/contribute/">Contribute</Link>
<Link href="/faq/">FAQ</Link>
<Link href="/report/">Report content</Link>
<a href={meta.repoUrl}>GitHub</a>
<a href="/llms.txt">llms.txt</a>
</nav>
Expand Down
74 changes: 74 additions & 0 deletions site/app/report/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import type { Metadata } from 'next';
import Link from 'next/link';
import Breadcrumbs from '@/components/Breadcrumbs';
import { meta } from '@/lib/content';
import { pageMeta } from '@/lib/seo';

// Notice-and-action mechanism (EU Digital Services Act Art. 16), point of
// contact for users (Art. 12) and copyright notices. Reports go to the privacy
// mailbox Better ISMS already publishes on its sites, or to a private GitHub
// security advisory for people who prefer GitHub.

const REPORT_EMAIL = 'privacy@ismscopilot.com';
const SUBJECT = 'Content report: grcskills';
const BODY = [
'Exact URL of the content:',
'',
'Why the content is illegal or infringes your rights (for copyright: the work and the rights you hold):',
'',
'Your name (optional for notices about child sexual abuse offences):',
'Your email (optional for notices about child sexual abuse offences):',
'',
'I confirm, in good faith, that the information and allegations in this notice are accurate and complete.',
'',
].join('\r\n');
const MAILTO = `mailto:${REPORT_EMAIL}?subject=${encodeURIComponent(SUBJECT)}&body=${encodeURIComponent(BODY)}`;

const FIELDS = [
'The exact URL of the content (a skill page, an author page, or a file on GitHub).',
'A clear explanation of why you believe the content is illegal or infringes your rights. For copyright, identify the work and the rights you hold.',
'Your name and email address. You may leave these out if your notice concerns child sexual abuse material or another offence covered by Articles 3 to 7 of Directive 2011/93/EU.',
'A statement that you believe, in good faith, that the information and allegations in your notice are accurate and complete.',
];

export const metadata: Metadata = pageMeta({
title: 'Report content',
description: 'How to report illegal content or a copyright issue on grcskills, and how to contact us.',
path: '/report/',
});

export default function Report() {
return (
<div className="page">
<Breadcrumbs items={[{ name: 'Report content', href: '/report/' }]} />
<header className="page-head">
<h1>Report content</h1>
<p className="lead">Report illegal content or a copyright issue in a skill or an author profile.</p>
</header>

<section className="section" aria-labelledby="what">
<div className="bio">
<h2 id="what">What you can report</h2>
<p>Skills and author profiles are written by contributors. If you believe something here is illegal, or uses your copyright or other rights without permission, tell us. This page is our notice mechanism under the EU Digital Services Act (Article 16) and the way to send us a copyright notice.</p>

<h2 id="include">What to include</h2>
<ul>
{FIELDS.map((f) => <li key={f}>{f}</li>)}
</ul>

<h2 id="send">How to send it</h2>
<p>Email <a href={MAILTO}>{REPORT_EMAIL}</a>. The link opens a message with the fields above already laid out. If you prefer GitHub, you can send the same information privately through a <a href={`${meta.repoUrl}/security/advisories/new`}>private GitHub advisory</a>; please do not put personal details in a public issue.</p>
<p>We review each notice promptly, carefully and objectively, and decide whether to act on it (for example by correcting or removing the content and cutting a new release). If you give us your email address, we confirm that we received your notice and tell you our decision without undue delay, with the reasons and how you can contest it. We may also tell the contributor what we removed and why.</p>
</div>
</section>

<section className="section close" aria-labelledby="contact">
<div>
<h2 id="contact">Point of contact</h2>
<p>The same address is our single point of contact for users of grcskills (Digital Services Act, Article 12). You can write to us in English or French. For a question about a skill, see the <Link href="/faq/">FAQ</Link>.</p>
</div>
<a className="b2" href={MAILTO}>Report content by email</a>
</section>
</div>
);
}
2 changes: 1 addition & 1 deletion site/scripts/build-content.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ export async function main() {
write('public/build.json', `${JSON.stringify({ sha, tag, builtAt: meta.builtAt, skills: skills.length }, null, 2)}\n`);

const urls = [
['/', allDates], ['/skills/', allDates], ['/contribute/', allDates], ['/faq/', allDates],
['/', allDates], ['/skills/', allDates], ['/contribute/', allDates], ['/faq/', allDates], ['/report/', allDates],
...skills.map((s) => [`/skill/${s.slug}/`, s.dateModified]),
...skills.flatMap((s) => s.references.map((r) => [`/skill/${s.slug}/${r.slug}/`, s.dateModified])),
...authors.filter((a) => a.skills.length).map((a) => [`/${a.slug}/`, a.dateModified]),
Expand Down
2 changes: 1 addition & 1 deletion site/scripts/content-lib.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const FRAMEWORK_LABELS = {
// Author slugs become top-level routes (/<author-slug>/), so they must not
// shadow a site route or a generated file.
export const RESERVED_SLUGS = new Set([
'skill', 'skills', 'category', 'categories', 'framework', 'frameworks', 'contribute', 'faq', 'about',
'skill', 'skills', 'category', 'categories', 'framework', 'frameworks', 'contribute', 'faq', 'report', 'about',
'search', 'api', 'llms', 'sitemap', 'robots', 'build', 'og', 'favicon', 'inline', 'static',
'assets', 'fonts', 'legal', 'privacy', 'terms', 'security', 'admin', 'login', 'index', '404',
]);
Expand Down
11 changes: 11 additions & 0 deletions site/tests/built.test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -364,3 +364,14 @@ test('top bar: copy at most 90 characters with the link, no em dash; topbar.js s
assert.doesNotMatch(js, /fetch|XMLHttpRequest|sendBeacon|document\.cookie/, 'no network, no cookies');
assert.ok(src.includes(`long: '${TOPBAR_LONG}'`) && src.includes(`short: '${TOPBAR_SHORT}'`), 'test copy matches lib/content.ts');
});

test('report: DSA notice page is built, linked, in the sitemap, with a CRLF prefilled mailto', opts, () => {
const html = read('report/index.html');
for (const field of ['exact URL', 'Your name and email address', 'good faith', 'Article 16', 'Article 12', 'Directive 2011/93/EU', 'how you can contest it']) {
assert.ok(html.includes(field), `report page mentions ${field}`);
}
assert.match(html, /href="mailto:privacy@ismscopilot\.com\?subject=[^"]+&amp;body=[^"]*%0D%0A/, 'prefilled mailto with CRLF line breaks');
assert.ok(read('index.html').includes('href="/report/"'), 'footer links the report page');
assert.ok(read('sitemap.xml').includes('<loc>https://grcskills.com/report/</loc>'), 'report in sitemap');
assert.doesNotMatch(html, /\u2014/, 'no em dash on report');
});
Loading