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
32 changes: 28 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ directement applicables.
- **TrustMap Audit** applique le référentiel de sécurité, priorise les écarts,
fournit les correctifs, gère les exceptions et conserve l’historique agrégé.
- **TrustMap CI** simule une politique sur l’inventaire courant et génère la
commande ainsi que le workflow GitHub Actions correspondant, avec SARIF,
CycloneDX, OSV et vérification de provenance configurables.
commande ainsi qu’un workflow GitHub Actions multi-environnements, avec
chemins, seuils, SARIF, CycloneDX, OSV et provenance configurables par profil.
- **TrustMap Enterprise** mesure la couverture des propriétaires et des preuves,
présente la posture par équipe et exporte un pack de gouvernance JSON.

Expand Down Expand Up @@ -65,6 +65,7 @@ connexion à un annuaire d’entreprise.
- priorisation par criticité ;
- remédiations expliquées avec extraits de configuration copiables ;
- historique persistant des scores et écarts, comparé audit par audit ;
- export CSV chronologique de la posture et des écarts agrégés ;
- exceptions de risque motivées, attribuées, datées et révocables ;
- export de rapports PDF, JSON, SARIF et d’un SBOM CycloneDX 1.7 ;
- vues dédiées aux serveurs, règles et audits ;
Expand Down Expand Up @@ -696,6 +697,29 @@ erreur d’entrée ou d’exécution, `2` pour une analyse réseau/provenance
incomplète et `3` pour une politique CI refusée. Si une analyse distante est
incomplète et que le seuil est aussi dépassé, le refus de politique (`3`) prime.

### Politiques par environnement et répertoire

TrustMap CI propose trois profils indépendants et modifiables :

| Profil | Chemin initial | Seuil initial | Contrôles réseau |
| --- | --- | --- | --- |
| Développement | `./.mcp/development.json` | critique | désactivés |
| Préproduction | `./.mcp/staging.json` | élevé | OSV et provenance |
| Production | `./.mcp/production.json` | modéré | OSV et provenance |

Chaque profil peut être inclus ou exclu du workflow. Le générateur produit un
job GitHub Actions séparé par environnement, des noms d’artefacts distincts et
une catégorie SARIF dédiée. Les chemins contenant des caractères de contrôle ou
des opérateurs shell sont refusés avant la génération.

### Exporter la tendance d’audit

Dans **TrustMap Audit → Historique & exceptions**, le bouton `Exporter CSV`
produit localement un fichier UTF-8 compatible avec Excel. Les points sont
ordonnés chronologiquement et incluent les variations de score, les écarts
introduits/résolus ainsi que les compteurs agrégés par règle. Aucun nom de
serveur, chemin de configuration, extrait ou secret n’est exporté.

## Limites actuelles

- la découverte doit être lancée explicitement sur chaque poste à inventorier ;
Expand Down Expand Up @@ -727,8 +751,8 @@ incomplète et que le seuil est aussi dépassé, le refus de politique (`3`) pri

## Prochaines étapes possibles

- politiques CI différenciées par environnement ou répertoire.
- export CSV de la tendance de posture et des écarts agrégés.
- signature et vérification des fichiers de politique CI exportés ;
- synchronisation chiffrée des exceptions entre membres d’un même espace.

## Contribution

Expand Down
16 changes: 13 additions & 3 deletions app/audit-history-view.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type AuditHistoryViewProps = {
loading: boolean;
error: string;
onClear: () => void;
onExportCsv: () => void;
};

const sourceLabel: Record<AuditHistorySource, string> = {
Expand All @@ -34,6 +35,7 @@ export function AuditHistoryView({
loading,
error,
onClear,
onExportCsv,
}: AuditHistoryViewProps) {
const trend = [...history].slice(0, 12).reverse();
const latest = history[0];
Expand Down Expand Up @@ -79,9 +81,17 @@ export function AuditHistoryView({
Synthèses pseudonymisées · configurations exclues
</small>
</div>
<button className="button secondary compact" onClick={onClear}>
Effacer l’historique
</button>
<div className="history-actions">
<button
className="button secondary compact"
onClick={onExportCsv}
>
Exporter CSV
</button>
<button className="button secondary compact" onClick={onClear}>
Effacer l’historique
</button>
</div>
</div>

<div className="history-layout">
Expand Down
112 changes: 112 additions & 0 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1487,6 +1487,101 @@ button.table-row:hover {
margin-top: 4px;
}

.policy-profile-grid {
display: grid;
gap: 12px;
grid-template-columns: repeat(3, minmax(0, 1fr));
}

.policy-profile-grid > article {
align-items: center;
background: var(--paper);
border: 1px solid var(--line);
border-radius: 13px;
display: flex;
min-width: 0;
padding: 10px;
transition:
border-color 160ms ease,
box-shadow 160ms ease,
opacity 160ms ease;
}

.policy-profile-grid > article.active {
border-color: var(--green);
box-shadow: 0 0 0 2px rgba(21, 122, 85, 0.1);
}

.policy-profile-grid > article.disabled {
opacity: 0.55;
}

.policy-profile-main {
align-items: center;
background: transparent;
border: 0;
display: flex;
flex: 1;
gap: 11px;
min-width: 0;
padding: 3px;
text-align: left;
}

.policy-profile-main > span {
align-items: center;
background: var(--green-soft);
border-radius: 9px;
color: var(--green-dark);
display: inline-flex;
flex: 0 0 auto;
font-size: 9px;
font-weight: 780;
height: 34px;
justify-content: center;
width: 34px;
}

.policy-profile-main > div {
min-width: 0;
}

.policy-profile-main small {
color: var(--subtle);
display: block;
font-size: 7px;
letter-spacing: 0.1em;
text-transform: uppercase;
}

.policy-profile-main strong {
display: block;
font-size: 11px;
margin: 2px 0;
}

.policy-profile-main p {
color: var(--muted);
font-size: 8px;
margin: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}

.policy-profile-grid > article > label {
align-items: center;
color: var(--muted);
display: flex;
flex: 0 0 auto;
font-size: 8px;
gap: 4px;
}

.policy-profile-grid > article > label input {
accent-color: var(--green);
}

.policy-builder {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -1947,6 +2042,11 @@ button.table-row:hover {
gap: 4px;
}

.history-actions {
display: flex;
gap: 8px;
}

.history-toolbar small {
color: var(--subtle);
font-size: 8px;
Expand Down Expand Up @@ -3646,6 +3746,10 @@ button.table-row:hover {
grid-template-columns: 1fr;
}

.policy-profile-grid {
grid-template-columns: 1fr;
}

.module-kpis article {
min-height: 94px;
}
Expand Down Expand Up @@ -3674,6 +3778,14 @@ button.table-row:hover {
.owner-row > span:nth-child(2) {
display: none;
}

.history-actions {
width: 100%;
}

.history-actions .button {
flex: 1;
}
}

@media (prefers-reduced-motion: reduce) {
Expand Down
16 changes: 16 additions & 0 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
import {
AUDIT_HISTORY_LIMIT,
createAuditHistoryPayload,
createAuditHistoryCsv,
parseAuditHistoryRecords,
type AuditHistoryRecord,
type AuditHistorySource,
Expand Down Expand Up @@ -811,6 +812,20 @@ export default function Home() {
}
};

const exportAuditHistoryCsv = () => {
const csv = createAuditHistoryCsv(auditHistory);
const downloadUrl = URL.createObjectURL(
new Blob([csv], { type: "text/csv;charset=utf-8" }),
);
const link = document.createElement("a");
link.href = downloadUrl;
link.download = `mcp-trustmap-history-${new Date().toISOString().slice(0, 10)}.csv`;
link.click();
URL.revokeObjectURL(downloadUrl);
setToast(`${auditHistory.length} points d’audit exportés en CSV`);
window.setTimeout(() => setToast(""), 2600);
};

const exportReport = async (
format: "json" | "sarif" | "cyclonedx" | "pdf",
) => {
Expand Down Expand Up @@ -1246,6 +1261,7 @@ export default function Home() {
loading={historyLoading}
error={historyError}
onClear={clearAuditHistory}
onExportCsv={exportAuditHistoryCsv}
/>
<article className="exception-register">
<div className="exception-register-head">
Expand Down
Loading