Skip to content

CCC Browsable #717

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 10 commits into from
Closed
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
16 changes: 16 additions & 0 deletions website/components.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "default",
"rsc": false,
"tsx": true,
"tailwind": {
"config": "tailwind.config.js",
"css": "src/css/custom.css",
"baseColor": "slate",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils"
}
}
8 changes: 6 additions & 2 deletions website/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,10 @@ const config: Config = {
],
],

plugins: [
'./src/plugin/ccc-pages/index.ts',
],

themeConfig: {
// Replace with your project's social card
image: 'img/logo/2023_FinosCCC_Horizontal.png',
Expand All @@ -59,12 +63,12 @@ const config: Config = {
label: 'Primer',
to: 'https://github.com/finos/common-cloud-controls/blob/main/docs/resources/training/FINOS-CCC-Primer-June-2024.pdf'
},
{ to: 'https://github.com/finos/common-cloud-controls/releases', label: 'Releases', position: 'left' },
{ to: '/ccc', label: 'CCC Catalog', position: 'left' },
{
href: 'https://github.com/finos/common-cloud-controls',
label: 'GitHub',
position: 'right',
},
}
],
},
footer: {
Expand Down
2,615 changes: 1,704 additions & 911 deletions website/package-lock.json

Large diffs are not rendered by default.

20 changes: 17 additions & 3 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,36 @@
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids",
"typecheck": "tsc"
"typecheck": "tsc",
"fetch:ccc": "ts-node --compiler-options '{\"module\":\"CommonJS\"}' scripts/DownloadCCCReleases.ts"
},
"dependencies": {
"@docusaurus/core": "3.7.0",
"@docusaurus/preset-classic": "3.7.0",
"@mdx-js/react": "^3.0.0",
"@radix-ui/react-avatar": "^1.1.4",
"@radix-ui/react-slot": "^1.2.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.0.0",
"js-yaml": "^4.1.0",
"prism-react-renderer": "^2.3.0",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-player": "^2.16.0"
"react-player": "^2.16.0",
"tailwind-merge": "^2.2.1",
"tailwind-variants": "^1.0.0",
"tailwindcss-animate": "^1.0.7"
},
"devDependencies": {
"@docusaurus/module-type-aliases": "3.7.0",
"@docusaurus/tsconfig": "3.7.0",
"@docusaurus/types": "3.7.0",
"@types/js-yaml": "^4.0.9",
"autoprefixer": "^10.4.21",
"axios": "^1.8.4",
"postcss": "^8.4.35",
"tailwindcss": "^3.4.1",
"ts-node": "^10.9.2",
"typescript": "~5.6.2"
},
"browserslist": {
Expand All @@ -45,4 +59,4 @@
"engines": {
"node": ">=18.0"
}
}
}
6 changes: 6 additions & 0 deletions website/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
53 changes: 53 additions & 0 deletions website/scripts/DownloadCCCReleases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import fs from 'fs';
import path from 'path';
import axios from 'axios';

const OUTPUT_DIR = path.join(__dirname, '../src/data/ccc-releases');
const GITHUB_API = 'https://api.github.com/repos/finos/common-cloud-controls/releases';
const GITHUB_TOKEN = process.env.GITHUB_TOKEN;

fs.mkdirSync(OUTPUT_DIR, { recursive: true });

interface ReleaseAsset {
name: string;
browser_download_url: string;
}

interface GitHubRelease {
assets: ReleaseAsset[];
}

async function downloadYamlAssets(): Promise<void> {
const headers = GITHUB_TOKEN ? { Authorization: `token ${GITHUB_TOKEN}` } : {};

console.log('📦 Fetching releases from GitHub...');
const response = await axios.get<GitHubRelease[]>(GITHUB_API, { headers });
const releases = response.data;

for (const release of releases) {
for (const asset of release.assets) {
if (asset.name.endsWith('.yaml')) {
const filePath = path.join(OUTPUT_DIR, asset.name);
console.log(`⬇️ Downloading ${asset.name} → ${filePath}`);

const download = await axios.get(asset.browser_download_url, {
responseType: 'stream',
headers,
});

const writer = fs.createWriteStream(filePath);
await new Promise<void>((resolve, reject) => {
download.data.pipe(writer);
writer.on('finish', resolve);
writer.on('error', reject);
});
}
}
}

console.log('✅ All YAML assets downloaded.');
}

downloadYamlAssets().catch((err) => {
console.error('❌ Error downloading YAML files:', err.message);
});
155 changes: 155 additions & 0 deletions website/src/components/ccc/Control/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import { TestRequirement } from "../TestRequirement";
import React from "react";
import Layout from "@theme/Layout";
import Link from "@docusaurus/Link";
import { Card, CardContent, CardHeader, CardTitle } from "../../ui/card";
import { Badge } from "../../ui/badge";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../../ui/table";
import { Threat } from "../Threat";
export interface Control {
id: string;
title: string;
objective: string;
control_family: string;
threats?: Threat[];
nist_csf?: string;
control_mappings?: ControlMappings;
test_requirements?: TestRequirement[];
link?: string;
}

interface ControlMappings {
[key: string]: string[];
}

interface PageData {
slug: string;
control: Control;
releaseTitle: string;
releaseId: string;
}

export default function CCCControlTemplate({ pageData }: { pageData: PageData }) {
const { control, slug, releaseTitle } = pageData;

return (
<Layout title={control.title}>
<main className="container margin-vert--lg space-y-6">
<Link to={`/ccc/${slug}`} className="text-primary hover:underline flex items-center gap-1">
← Back to {releaseTitle}
</Link>

<Card>
<CardHeader>
<CardTitle>
{control.id}: {control.title}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<div className="flex items-center gap-2">
<span className="font-medium">Objective:</span>
<span>{control.objective}</span>
</div>
<div className="flex items-center gap-2">
<span className="font-medium">Control Family:</span>
<Badge variant="secondary">{control.control_family}</Badge>
</div>

{control.threats?.length > 0 && (
<div className="space-y-2">
<span className="font-medium">Threats:</span>
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead>Title</TableHead>
<TableHead>Description</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{control.threats.map((threat) => (
<TableRow key={threat.id}>
<TableCell>
<Link to={`/ccc/${slug}/${threat.id}`} className="text-primary hover:underline">
{threat.id}
</Link>
</TableCell>
<TableCell>{threat.title}</TableCell>
<TableCell>{threat.description}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
)}

{control.nist_csf && (
<div className="flex items-center gap-2">
<span className="font-medium">NIST CSF:</span>
<Badge variant="outline">{control.nist_csf}</Badge>
</div>
)}
</div>
</CardContent>
</Card>

{control.control_mappings && (
<Card>
<CardHeader>
<CardTitle>Control Mappings</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-2">
{Object.entries(control.control_mappings).map(([framework, values]) => (
<div key={framework} className="flex items-center gap-2">
<span className="font-medium">{framework}:</span>
<div className="flex flex-wrap gap-2">
{values.map((value) => (
<Badge key={value} variant="outline">
{value}
</Badge>
))}
</div>
</div>
))}
</div>
</CardContent>
</Card>
)}

{control.test_requirements?.length > 0 && (
<Card>
<CardHeader>
<CardTitle>Test Requirements</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-4">
{control.test_requirements.map((tr) => (
<div key={tr.id} className="space-y-2">
<div className="flex items-center gap-2">
<span className="font-medium">{tr.id}:</span>
<span>{tr.text}</span>
</div>
{tr.tlp_levels?.length > 0 && (
<div className="flex items-center gap-2">
<span className="text-sm text-muted-foreground">TLP:</span>
<div className="flex flex-wrap gap-2">
{tr.tlp_levels.map((level) => (
<Badge key={level} variant="outline">
{level}
</Badge>
))}
</div>
</div>
)}
</div>
))}
</div>
</CardContent>
</Card>
)}
</main>
</Layout>
);
}
Loading