@bluefly/openstandardagents 0.2.7 ā 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.devfile.yaml +1 -1
- package/.env.example +1 -1
- package/.github/AGENTS.md +245 -0
- package/.github/agents/github-issue-triage.ossa.yaml +99 -0
- package/.github/agents/github-pr-triage.ossa.yaml +137 -0
- package/.github/workflows/issue-sync-to-gitlab.yml +138 -0
- package/.github/workflows/pr-triage-to-gitlab.yml +164 -0
- package/.version.json +2 -2
- package/.wiki-config.json +24 -0
- package/CHANGELOG.md +44 -18
- package/CODEOWNERS +75 -0
- package/CONTRIBUTING.md +103 -4
- package/README.md +178 -243
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/repositories/schema.repository.d.ts +6 -1
- package/dist/repositories/schema.repository.d.ts.map +1 -1
- package/dist/repositories/schema.repository.js +49 -27
- package/dist/repositories/schema.repository.js.map +1 -1
- package/dist/services/migration.service.d.ts +4 -3
- package/dist/services/migration.service.d.ts.map +1 -1
- package/dist/services/migration.service.js +11 -10
- package/dist/services/migration.service.js.map +1 -1
- package/dist/services/release-automation/release.service.js +1 -1
- package/dist/services/release-automation/release.service.js.map +1 -1
- package/dist/services/release-automation/schemas/release.schema.js +1 -1
- package/dist/services/release-automation/webhook.service.js +3 -3
- package/dist/services/release-automation/webhook.service.js.map +1 -1
- package/dist/services/runtime/claude/claude-adapter.d.ts +1 -1
- package/dist/services/runtime/claude/claude-adapter.d.ts.map +1 -1
- package/dist/services/runtime/claude/claude-adapter.js +2 -2
- package/dist/services/runtime/claude/claude-adapter.js.map +1 -1
- package/dist/spec/v0.2.8/CHANGELOG.md +401 -0
- package/dist/spec/v0.2.8/README.md +72 -0
- package/dist/spec/v0.2.8/migrations/v0.2.3-to-v0.2.4.md +599 -0
- package/dist/spec/v0.2.8/migrations/v0.2.5-RC-to-v0.2.6.md +65 -0
- package/dist/spec/v0.2.8/migrations/v0.2.6-to-v0.2.8.md +81 -0
- package/{spec/v0.2.6-dev/ossa-0.2.5.schema.json ā dist/spec/v0.2.8/ossa-0.2.8.schema.json} +1509 -52
- package/dist/spec/v0.2.8/ossa-0.2.8.yaml +581 -0
- package/dist/spec/v0.2.9/a2a-protocol.md +1337 -0
- package/dist/spec/v0.2.9/agent.md +1946 -0
- package/dist/spec/v0.2.9/capabilities/index.yaml +25 -0
- package/dist/spec/v0.2.9/capabilities/memory.yaml +251 -0
- package/dist/spec/v0.2.9/capability-schema.md +576 -0
- package/dist/spec/v0.2.9/compliance-profiles.md +533 -0
- package/dist/spec/v0.2.9/conformance-testing.md +1527 -0
- package/dist/spec/v0.2.9/gitlab-duo-integration.md +621 -0
- package/dist/spec/v0.2.9/ossa-0.2.9.schema.json +3699 -0
- package/dist/spec/v0.2.9/runtime-semantics.md +464 -0
- package/dist/spec/v0.2.9/security-model.md +1245 -0
- package/dist/spec/v0.2.9/semantic-conventions.md +347 -0
- package/dist/spec/v0.2.9/types.ts +522 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/policy.d.ts +377 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/policy.js +84 -0
- package/dist/types/policy.js.map +1 -0
- package/dist/utils/index.d.ts +6 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/version.d.ts +68 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +156 -0
- package/dist/utils/version.js.map +1 -0
- package/docs/specs/policy-dsl.md +925 -0
- package/eslint-report.json +1 -0
- package/examples/adk-integration/code-review-workflow.yml +1 -1
- package/examples/adk-integration/customer-support.yml +1 -1
- package/examples/adk-integration/data-pipeline.yml +1 -1
- package/examples/advanced/reasoning-agent.yaml +136 -0
- package/examples/advanced/workflows/hybrid-model-strategy.yaml +1 -1
- package/examples/agent-manifests/critics/critic-agent.yaml +1 -1
- package/examples/agent-manifests/governors/governor-agent.yaml +1 -1
- package/examples/agent-manifests/integrators/integrator-agent.yaml +1 -1
- package/examples/agent-manifests/judges/judge-agent.yaml +1 -1
- package/examples/agent-manifests/monitors/monitor-agent.yaml +1 -1
- package/examples/agent-manifests/orchestrators/orchestrator-agent.yaml +1 -1
- package/examples/agent-manifests/sample-compliant-agent.yaml +1 -1
- package/examples/agent-manifests/workers/worker-agent.yaml +1 -1
- package/examples/agents-md/code-agent.ossa.json +100 -0
- package/examples/agents-md/monorepo-agent.ossa.yaml +180 -0
- package/examples/anthropic/claude-assistant.ossa.json +1 -1
- package/examples/autogen/multi-agent.ossa.json +1 -1
- package/examples/claude-code/code-reviewer.ossa.yaml +78 -0
- package/examples/claude-code/ossa-validator.ossa.yaml +80 -0
- package/examples/common_npm/agent-router.ossa.yaml +1 -0
- package/examples/common_npm/agent-router.v0.2.2.ossa.yaml +1 -1
- package/examples/crewai/research-team.ossa.json +1 -1
- package/examples/cursor/code-review-agent.ossa.json +1 -1
- package/examples/drupal/gitlab-ml-recommender.ossa.yaml +1 -0
- package/examples/drupal/gitlab-ml-recommender.v0.2.2.ossa.yaml +1 -1
- package/examples/extensions/agents-md-v1.yml +175 -0
- package/examples/extensions/drupal-v1.yml +1 -1
- package/examples/extensions/kagent-v1.yml +1 -1
- package/examples/getting-started/hello-world-complete.ossa.yaml +1 -1
- package/examples/integration-patterns/agent-to-agent-orchestration.ossa.yaml +4 -4
- package/examples/kagent/compliance-validator.ossa.yaml +1 -1
- package/examples/kagent/cost-optimizer.ossa.yaml +1 -1
- package/examples/kagent/documentation-agent.ossa.yaml +1 -1
- package/examples/kagent/k8s-troubleshooter-v1.ossa.yaml +1 -0
- package/examples/kagent/k8s-troubleshooter-v1.v0.2.2.ossa.yaml +1 -1
- package/examples/kagent/k8s-troubleshooter.ossa.yaml +1 -1
- package/examples/kagent/security-scanner.ossa.yaml +1 -1
- package/examples/langchain/chain-agent.ossa.json +1 -1
- package/examples/langflow/workflow-agent.ossa.json +1 -1
- package/examples/langgraph/state-machine-agent.ossa.json +1 -1
- package/examples/llamaindex/rag-agent.ossa.json +1 -1
- package/examples/migration-guides/from-langchain-to-ossa.yaml +4 -4
- package/examples/multi-agent/README.md +74 -0
- package/examples/multi-agent/conditional-router.ossa.yaml +42 -0
- package/examples/multi-agent/parallel-execution.ossa.yaml +54 -0
- package/examples/multi-agent/sequential-pipeline.ossa.yaml +45 -0
- package/examples/openai/basic-agent.ossa.yaml +1 -1
- package/examples/openai/multi-tool-agent.ossa.json +1 -1
- package/examples/openai/swarm-agent.ossa.json +1 -1
- package/examples/production/document-analyzer-openai.yml +1 -1
- package/examples/quickstart/support-agent.ossa.yaml +1 -1
- package/examples/spec-examples/audit-agent.yml +1 -1
- package/examples/spec-examples/chat-agent.yml +1 -1
- package/examples/spec-examples/compliance-agent.yml +1 -1
- package/examples/spec-examples/monitoring-agent.yml +1 -1
- package/examples/spec-examples/workflow-agent.yml +1 -1
- package/examples/templates/ossa-compliance.yaml +1 -1
- package/examples/vercel/edge-agent.ossa.json +1 -1
- package/gl-code-quality-report.json +62 -0
- package/llms-ctx-full.txt +39 -0
- package/llms-ctx.txt +39 -0
- package/llms.txt +47 -0
- package/package.json +6 -3
- package/scripts/README.md +25 -0
- package/scripts/compliance-audit.ts +796 -0
- package/scripts/eslint-to-codequality.cjs +34 -0
- package/scripts/generate-agents-catalog.ts +2 -1
- package/scripts/generate-api-docs.ts +2 -1
- package/scripts/generate-examples-docs.ts +2 -1
- package/scripts/generate-llms-ctx.sh +17 -0
- package/scripts/generate-schema-docs.ts +31 -10
- package/scripts/sync-version.js +4 -12
- package/scripts/validate-schema.ts +2 -1
- package/spec/v0.2.8/CHANGELOG.md +401 -0
- package/spec/v0.2.8/README.md +72 -0
- package/spec/v0.2.8/migrations/v0.2.3-to-v0.2.4.md +599 -0
- package/spec/v0.2.8/migrations/v0.2.5-RC-to-v0.2.6.md +65 -0
- package/spec/v0.2.8/migrations/v0.2.6-to-v0.2.8.md +81 -0
- package/spec/{v0.2.6-dev/ossa-0.2.6-dev.schema.json ā v0.2.8/ossa-0.2.8.schema.json} +1509 -52
- package/spec/v0.2.8/ossa-0.2.8.yaml +581 -0
- package/spec/v0.2.9/a2a-protocol.md +1337 -0
- package/spec/v0.2.9/agent.md +1946 -0
- package/spec/v0.2.9/capabilities/index.yaml +25 -0
- package/spec/v0.2.9/capabilities/memory.yaml +251 -0
- package/spec/v0.2.9/capability-schema.md +576 -0
- package/spec/v0.2.9/compliance-profiles.md +533 -0
- package/spec/v0.2.9/conformance-testing.md +1527 -0
- package/spec/v0.2.9/gitlab-duo-integration.md +621 -0
- package/spec/v0.2.9/ossa-0.2.9.schema.json +3699 -0
- package/spec/v0.2.9/runtime-semantics.md +464 -0
- package/spec/v0.2.9/security-model.md +1245 -0
- package/spec/v0.2.9/semantic-conventions.md +347 -0
- package/spec/v0.2.9/types.ts +522 -0
- package/test-results/junit.xml +337 -0
- package/.github/workflows/pr-comment.yml +0 -33
- package/bin/validate-ossa-0.2.5-RC.ts +0 -244
- package/dist/spec/v0.2.6-dev/ossa-0.2.5.schema.json +0 -1696
- package/dist/spec/v0.2.6-dev/ossa-0.2.6-dev.schema.json +0 -1696
- package/scripts/lib/exec.ts +0 -37
- package/scripts/lib/file-ops.ts +0 -58
- package/scripts/lib/version.ts +0 -83
- package/website/.lighthouserc.ts +0 -24
- package/website/.prettierrc +0 -10
- package/website/DESIGN_SYSTEM_IMPLEMENTATION.md +0 -445
- package/website/Dockerfile +0 -30
- package/website/app/about/page.tsx +0 -304
- package/website/app/blog/[slug]/page.tsx +0 -208
- package/website/app/blog/page.tsx +0 -249
- package/website/app/design-guide/page.tsx +0 -511
- package/website/app/docs/[[...slug]]/page.tsx +0 -847
- package/website/app/docs/core-concepts/project-structure/page.tsx +0 -349
- package/website/app/ecosystem/page.tsx +0 -410
- package/website/app/examples/page.tsx +0 -133
- package/website/app/globals.scss +0 -370
- package/website/app/layout.tsx +0 -106
- package/website/app/license/page.tsx +0 -183
- package/website/app/not-found.tsx +0 -18
- package/website/app/page.tsx +0 -686
- package/website/app/page.tsx.bak +0 -679
- package/website/app/page.tsx.bak2 +0 -649
- package/website/app/playground/page.tsx +0 -487
- package/website/app/robots.ts +0 -19
- package/website/app/rss.xml/route.ts +0 -74
- package/website/app/schema/page.tsx +0 -1001
- package/website/app/sitemap.ts +0 -56
- package/website/app/specification/page.tsx +0 -287
- package/website/components/InstallCommand.tsx +0 -96
- package/website/components/Logo.tsx +0 -97
- package/website/components/StructuredData.tsx +0 -65
- package/website/components/docs/DocsSearch.tsx +0 -104
- package/website/components/docs/DocsSidebar.tsx +0 -155
- package/website/components/docs/MarkdownContent.tsx +0 -401
- package/website/components/docs/VersionSelector.tsx +0 -105
- package/website/components/examples/ExamplesViewer.tsx +0 -293
- package/website/components/layout/Footer.tsx +0 -116
- package/website/components/layout/Header.tsx +0 -172
- package/website/components/schema/SchemaComponentsAccordion.tsx +0 -84
- package/website/components/schema/SchemaExplorer.tsx +0 -213
- package/website/components/ui/Badge.tsx +0 -82
- package/website/components/ui/Button.tsx +0 -116
- package/website/components/ui/Card.tsx +0 -167
- package/website/components/ui/Checkbox.tsx +0 -141
- package/website/components/ui/Input.tsx +0 -169
- package/website/components/ui/Radio.tsx +0 -141
- package/website/components/ui/Select.tsx +0 -182
- package/website/components/ui/Tag.tsx +0 -158
- package/website/components/ui/Textarea.tsx +0 -195
- package/website/components/ui/index.ts +0 -11
- package/website/content/blog/OpenAPI-AI-Agents-Standard.md +0 -285
- package/website/content/blog/Why-Formal-Standards-Matter-Now.md +0 -198
- package/website/content/blog/gitlab-kubernetes-agent-ecosystem.md +0 -286
- package/website/content/blog/introducing-ossa-framework.md +0 -328
- package/website/content/blog/ossa-production-results.md +0 -279
- package/website/content/blog/welcome-to-ossa.md +0 -43
- package/website/content/blog/why-ai-agents-need-open-standard.md +0 -98
- package/website/content/docs/00-home.md +0 -153
- package/website/content/docs/adapters/openai-adapter.md +0 -693
- package/website/content/docs/agents/catalog.md +0 -28
- package/website/content/docs/aiflow-framework-integration-with-ossa.md +0 -107
- package/website/content/docs/api-reference/index.md +0 -38
- package/website/content/docs/api-reference/ossa-core-api.md +0 -634
- package/website/content/docs/api-reference/ossa-registry-api.md +0 -515
- package/website/content/docs/api-reference/unified-agent-gateway.md +0 -599
- package/website/content/docs/architecture/execution-flow.md +0 -335
- package/website/content/docs/architecture/multi-agent-systems.md +0 -737
- package/website/content/docs/architecture/overview.md +0 -121
- package/website/content/docs/architecture/stack-integration.md +0 -461
- package/website/content/docs/changelog.md +0 -246
- package/website/content/docs/cli-reference/index.md +0 -111
- package/website/content/docs/cli-reference/ossa-agents.md +0 -70
- package/website/content/docs/cli-reference/ossa-export.md +0 -56
- package/website/content/docs/cli-reference/ossa-generate.md +0 -66
- package/website/content/docs/cli-reference/ossa-gitlab-agent.md +0 -57
- package/website/content/docs/cli-reference/ossa-import.md +0 -56
- package/website/content/docs/cli-reference/ossa-init.md +0 -57
- package/website/content/docs/cli-reference/ossa-migrate.md +0 -62
- package/website/content/docs/cli-reference/ossa-run.md +0 -66
- package/website/content/docs/cli-reference/ossa-schema.md +0 -57
- package/website/content/docs/cli-reference/ossa-setup.md +0 -57
- package/website/content/docs/cli-reference/ossa-validate.md +0 -66
- package/website/content/docs/configuration/index.md +0 -97
- package/website/content/docs/contributing.md +0 -599
- package/website/content/docs/deployment/github-mirroring.md +0 -924
- package/website/content/docs/documentation.md +0 -100
- package/website/content/docs/ecosystem/framework-support.md +0 -1361
- package/website/content/docs/ecosystem/overview.md +0 -366
- package/website/content/docs/errors/index.md +0 -10
- package/website/content/docs/examples/aiflow-framework-integration-with-ossa.md +0 -107
- package/website/content/docs/examples/catalog.md +0 -300
- package/website/content/docs/for-audiences/students-researchers.md +0 -122
- package/website/content/docs/getting-started/index.md +0 -92
- package/website/content/docs/getting-started/installation.md +0 -155
- package/website/content/docs/getting-started/running-agents.md +0 -309
- package/website/content/docs/getting-started.md +0 -91
- package/website/content/docs/integrations/aiflow.md +0 -104
- package/website/content/docs/integrations/drupal.md +0 -105
- package/website/content/docs/migration-guides/agent-schema-comparison.md +0 -232
- package/website/content/docs/migration-guides/anthropic-mcp-to-ossa.md +0 -1750
- package/website/content/docs/migration-guides/crewai-to-ossa.md +0 -274
- package/website/content/docs/migration-guides/drupal-eca-to-ossa.md +0 -2017
- package/website/content/docs/migration-guides/general-agent-schema.yml +0 -247
- package/website/content/docs/migration-guides/index.md +0 -133
- package/website/content/docs/migration-guides/langchain-to-ossa.md +0 -1714
- package/website/content/docs/migration-guides/langflow-to-ossa.md +0 -2075
- package/website/content/docs/migration-guides/migration-manifest.json +0 -64
- package/website/content/docs/migration-guides/openai-to-ossa.md +0 -1202
- package/website/content/docs/openapi-extensions/examples.md +0 -550
- package/website/content/docs/openapi-extensions/index.md +0 -551
- package/website/content/docs/openapi-extensions/operation-extensions.md +0 -457
- package/website/content/docs/openapi-extensions/root-extensions.md +0 -410
- package/website/content/docs/ossa-compliant-badge.md +0 -251
- package/website/content/docs/pre-release/index.md +0 -175
- package/website/content/docs/quick-reference.md +0 -17
- package/website/content/docs/readme.md +0 -35
- package/website/content/docs/releases/v0.2.6.md +0 -99
- package/website/content/docs/schema-reference/agent-capabilities.md +0 -50
- package/website/content/docs/schema-reference/agent-id.md +0 -52
- package/website/content/docs/schema-reference/agent-name.md +0 -50
- package/website/content/docs/schema-reference/agent-role.md +0 -54
- package/website/content/docs/schema-reference/agent-spec.md +0 -406
- package/website/content/docs/schema-reference/agent-version.md +0 -50
- package/website/content/docs/schema-reference/autonomy.md +0 -568
- package/website/content/docs/schema-reference/constraints.md +0 -543
- package/website/content/docs/schema-reference/index.md +0 -45
- package/website/content/docs/schema-reference/llm-config.md +0 -445
- package/website/content/docs/schema-reference/observability.md +0 -654
- package/website/content/docs/schema-reference/ossa-manifest.md +0 -309
- package/website/content/docs/schema-reference/taxonomy.md +0 -509
- package/website/content/docs/schema-reference/tools.md +0 -628
- package/website/content/docs/templates/blog-post.md +0 -43
- package/website/content/docs/types-reference/index.md +0 -105
- package/website/content/docs/use-cases/00-index.md +0 -395
- package/website/content/docs/use-cases/cicd-code-review.md +0 -1236
- package/website/content/docs/use-cases/customer-support.md +0 -1234
- package/website/content/docs/use-cases/enterprise-compliance.md +0 -1208
- package/website/content/docs/use-cases/research-multi-agent.md +0 -1161
- package/website/content/docs/versioning.md +0 -288
- package/website/dev.sh +0 -53
- package/website/docker-compose.dev.yml +0 -36
- package/website/lib/version.ts +0 -35
- package/website/lib/versions.json +0 -103
- package/website/next.config.ts +0 -18
- package/website/nginx.conf +0 -32
- package/website/package-lock.json +0 -9679
- package/website/package.json +0 -59
- package/website/postcss.config.mjs +0 -9
- package/website/scripts/fetch-versions.js +0 -166
- package/website/scripts/generate-examples-index.js +0 -163
- package/website/scripts/merge-docs-to-wiki.ts +0 -207
- package/website/scripts/sync-version.js +0 -72
- package/website/scripts/sync-wiki.ts +0 -322
- package/website/scripts/upload-wiki.ts +0 -199
- package/website/styles/_spacing.scss +0 -453
- package/website/styles/_tokens.scss +0 -245
- package/website/styles/_typography.scss +0 -361
- package/website/styles/_variables.scss +0 -287
- package/website/tailwind.config.ts +0 -170
|
@@ -0,0 +1,796 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* GitLab Ultimate Compliance Audit System
|
|
4
|
+
*
|
|
5
|
+
* Performs automated compliance checks for GitLab projects:
|
|
6
|
+
* - Protected branches configuration
|
|
7
|
+
* - Merge request approval rules
|
|
8
|
+
* - Security scanning (SAST, dependency scanning, container scanning)
|
|
9
|
+
* - DORA metrics configuration
|
|
10
|
+
* - Compliance frameworks
|
|
11
|
+
* - Pipeline configuration
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* tsx scripts/compliance-audit.ts [project-id]
|
|
15
|
+
*
|
|
16
|
+
* Environment Variables:
|
|
17
|
+
* GITLAB_TOKEN - GitLab API token (required)
|
|
18
|
+
* GITLAB_HOST - GitLab instance URL (default: https://gitlab.com)
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { Gitlab } from '@gitbeaker/rest';
|
|
22
|
+
import { writeFileSync } from 'fs';
|
|
23
|
+
|
|
24
|
+
// Configuration
|
|
25
|
+
const DEFAULT_PROJECT = 'blueflyio/openstandardagents';
|
|
26
|
+
const GITLAB_HOST = process.env.GITLAB_HOST?.replace('gitlab.com', 'https://gitlab.com') || 'https://gitlab.com';
|
|
27
|
+
const TOKEN = process.env.GITLAB_TOKEN || process.env.SERVICE_ACCOUNT_OSSA_TOKEN || process.env.GITLAB_PUSH_TOKEN;
|
|
28
|
+
|
|
29
|
+
if (!TOKEN) {
|
|
30
|
+
console.error('ā Error: GITLAB_TOKEN required');
|
|
31
|
+
console.error(' Set environment variable: export GITLAB_TOKEN=<your-token>');
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const gitlab = new Gitlab({
|
|
36
|
+
host: GITLAB_HOST.replace(/\/$/, ''),
|
|
37
|
+
token: TOKEN,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Compliance check results interface
|
|
41
|
+
interface ComplianceCheck {
|
|
42
|
+
category: string;
|
|
43
|
+
check: string;
|
|
44
|
+
status: 'pass' | 'fail' | 'warning' | 'info';
|
|
45
|
+
message: string;
|
|
46
|
+
remediation?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface AuditReport {
|
|
50
|
+
projectId: string;
|
|
51
|
+
projectName: string;
|
|
52
|
+
auditDate: string;
|
|
53
|
+
overallStatus: 'compliant' | 'non-compliant' | 'partial';
|
|
54
|
+
checks: ComplianceCheck[];
|
|
55
|
+
summary: {
|
|
56
|
+
total: number;
|
|
57
|
+
passed: number;
|
|
58
|
+
failed: number;
|
|
59
|
+
warnings: number;
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Helper functions
|
|
64
|
+
function createCheck(
|
|
65
|
+
category: string,
|
|
66
|
+
check: string,
|
|
67
|
+
status: 'pass' | 'fail' | 'warning' | 'info',
|
|
68
|
+
message: string,
|
|
69
|
+
remediation?: string
|
|
70
|
+
): ComplianceCheck {
|
|
71
|
+
return { category, check, status, message, remediation };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getStatusIcon(status: string): string {
|
|
75
|
+
switch (status) {
|
|
76
|
+
case 'pass': return 'ā
';
|
|
77
|
+
case 'fail': return 'ā';
|
|
78
|
+
case 'warning': return 'ā ļø';
|
|
79
|
+
case 'info': return 'ā¹ļø';
|
|
80
|
+
default: return 'ā¢';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Audit functions
|
|
85
|
+
async function auditProtectedBranches(projectId: string): Promise<ComplianceCheck[]> {
|
|
86
|
+
const checks: ComplianceCheck[] = [];
|
|
87
|
+
const category = 'Branch Protection';
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const protectedBranches = await gitlab.ProtectedBranches.all(projectId);
|
|
91
|
+
|
|
92
|
+
// Check main branch protection
|
|
93
|
+
const mainProtected = protectedBranches.find((pb: any) => pb.name === 'main' || pb.name === 'master');
|
|
94
|
+
if (mainProtected) {
|
|
95
|
+
checks.push(createCheck(
|
|
96
|
+
category,
|
|
97
|
+
'Main Branch Protected',
|
|
98
|
+
'pass',
|
|
99
|
+
`Main branch is protected with ${mainProtected.push_access_levels?.length || 0} push rules and ${mainProtected.merge_access_levels?.length || 0} merge rules`
|
|
100
|
+
));
|
|
101
|
+
|
|
102
|
+
// Check force push is disabled
|
|
103
|
+
if (mainProtected.allow_force_push === false) {
|
|
104
|
+
checks.push(createCheck(
|
|
105
|
+
category,
|
|
106
|
+
'Force Push Disabled',
|
|
107
|
+
'pass',
|
|
108
|
+
'Force push is disabled on main branch'
|
|
109
|
+
));
|
|
110
|
+
} else {
|
|
111
|
+
checks.push(createCheck(
|
|
112
|
+
category,
|
|
113
|
+
'Force Push Disabled',
|
|
114
|
+
'fail',
|
|
115
|
+
'Force push is enabled on main branch',
|
|
116
|
+
'Disable force push: Settings > Repository > Protected branches'
|
|
117
|
+
));
|
|
118
|
+
}
|
|
119
|
+
} else {
|
|
120
|
+
checks.push(createCheck(
|
|
121
|
+
category,
|
|
122
|
+
'Main Branch Protected',
|
|
123
|
+
'fail',
|
|
124
|
+
'Main branch is not protected',
|
|
125
|
+
'Protect main branch: Settings > Repository > Protected branches'
|
|
126
|
+
));
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Check development branch protection
|
|
130
|
+
const devProtected = protectedBranches.find((pb: any) => pb.name === 'development' || pb.name === 'develop');
|
|
131
|
+
if (devProtected) {
|
|
132
|
+
checks.push(createCheck(
|
|
133
|
+
category,
|
|
134
|
+
'Development Branch Protected',
|
|
135
|
+
'pass',
|
|
136
|
+
'Development branch is protected'
|
|
137
|
+
));
|
|
138
|
+
} else {
|
|
139
|
+
checks.push(createCheck(
|
|
140
|
+
category,
|
|
141
|
+
'Development Branch Protected',
|
|
142
|
+
'warning',
|
|
143
|
+
'Development branch is not protected',
|
|
144
|
+
'Consider protecting development branch for better workflow control'
|
|
145
|
+
));
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
} catch (error: any) {
|
|
149
|
+
checks.push(createCheck(
|
|
150
|
+
category,
|
|
151
|
+
'Branch Protection Check',
|
|
152
|
+
'fail',
|
|
153
|
+
`Failed to check branch protection: ${error.message}`
|
|
154
|
+
));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return checks;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function auditMergeRequestApprovals(projectId: string): Promise<ComplianceCheck[]> {
|
|
161
|
+
const checks: ComplianceCheck[] = [];
|
|
162
|
+
const category = 'Merge Request Approvals';
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// Get project settings to check approval requirements
|
|
166
|
+
const project = await gitlab.Projects.show(projectId);
|
|
167
|
+
|
|
168
|
+
// Check if approvals are required (available in Free tier via project settings)
|
|
169
|
+
if (project.approvals_before_merge && project.approvals_before_merge > 0) {
|
|
170
|
+
checks.push(createCheck(
|
|
171
|
+
category,
|
|
172
|
+
'Approvals Required',
|
|
173
|
+
'pass',
|
|
174
|
+
`${project.approvals_before_merge} approval(s) required before merge`
|
|
175
|
+
));
|
|
176
|
+
} else {
|
|
177
|
+
checks.push(createCheck(
|
|
178
|
+
category,
|
|
179
|
+
'Approvals Required',
|
|
180
|
+
'warning',
|
|
181
|
+
'No approvals required before merge',
|
|
182
|
+
'Consider requiring at least 1 approval: Settings > Merge requests > Approvals'
|
|
183
|
+
));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Try to get advanced approval rules (Premium/Ultimate feature)
|
|
187
|
+
try {
|
|
188
|
+
const rules = await gitlab.ProjectApprovalRules.all(projectId);
|
|
189
|
+
if (rules && rules.length > 0) {
|
|
190
|
+
checks.push(createCheck(
|
|
191
|
+
category,
|
|
192
|
+
'Approval Rules Defined',
|
|
193
|
+
'pass',
|
|
194
|
+
`${rules.length} approval rule(s) defined`
|
|
195
|
+
));
|
|
196
|
+
|
|
197
|
+
// Check for CODEOWNERS integration
|
|
198
|
+
const hasCodeOwners = rules.some((rule: any) => rule.rule_type === 'code_owner');
|
|
199
|
+
if (hasCodeOwners) {
|
|
200
|
+
checks.push(createCheck(
|
|
201
|
+
category,
|
|
202
|
+
'Code Owner Approvals',
|
|
203
|
+
'pass',
|
|
204
|
+
'Code owner approval rules are configured'
|
|
205
|
+
));
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
checks.push(createCheck(
|
|
209
|
+
category,
|
|
210
|
+
'Approval Rules Defined',
|
|
211
|
+
'info',
|
|
212
|
+
'No specific approval rules defined',
|
|
213
|
+
'Advanced approval rules require GitLab Premium/Ultimate'
|
|
214
|
+
));
|
|
215
|
+
}
|
|
216
|
+
} catch (rulesError: any) {
|
|
217
|
+
// Advanced rules not available or not accessible
|
|
218
|
+
checks.push(createCheck(
|
|
219
|
+
category,
|
|
220
|
+
'Advanced Approval Rules',
|
|
221
|
+
'info',
|
|
222
|
+
'Advanced approval rules not available',
|
|
223
|
+
'This feature requires GitLab Premium/Ultimate'
|
|
224
|
+
));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Check merge request settings
|
|
228
|
+
if (project.only_allow_merge_if_pipeline_succeeds) {
|
|
229
|
+
checks.push(createCheck(
|
|
230
|
+
category,
|
|
231
|
+
'Pipeline Success Required',
|
|
232
|
+
'pass',
|
|
233
|
+
'Merge is only allowed if pipeline succeeds'
|
|
234
|
+
));
|
|
235
|
+
} else {
|
|
236
|
+
checks.push(createCheck(
|
|
237
|
+
category,
|
|
238
|
+
'Pipeline Success Required',
|
|
239
|
+
'warning',
|
|
240
|
+
'Merges are allowed without successful pipeline',
|
|
241
|
+
'Enable: Settings > Merge requests > Pipelines must succeed'
|
|
242
|
+
));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
if (project.only_allow_merge_if_all_discussions_are_resolved) {
|
|
246
|
+
checks.push(createCheck(
|
|
247
|
+
category,
|
|
248
|
+
'Discussions Must Be Resolved',
|
|
249
|
+
'pass',
|
|
250
|
+
'All discussions must be resolved before merge'
|
|
251
|
+
));
|
|
252
|
+
} else {
|
|
253
|
+
checks.push(createCheck(
|
|
254
|
+
category,
|
|
255
|
+
'Discussions Must Be Resolved',
|
|
256
|
+
'info',
|
|
257
|
+
'Merges are allowed with unresolved discussions',
|
|
258
|
+
'Enable: Settings > Merge requests > All discussions must be resolved'
|
|
259
|
+
));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
} catch (error: any) {
|
|
263
|
+
checks.push(createCheck(
|
|
264
|
+
category,
|
|
265
|
+
'MR Approvals Check',
|
|
266
|
+
'warning',
|
|
267
|
+
`Could not check approval settings: ${error.message}`,
|
|
268
|
+
'This may require GitLab Premium/Ultimate'
|
|
269
|
+
));
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return checks;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function auditSecurityScanning(projectId: string): Promise<ComplianceCheck[]> {
|
|
276
|
+
const checks: ComplianceCheck[] = [];
|
|
277
|
+
const category = 'Security Scanning';
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
// Get CI/CD configuration
|
|
281
|
+
const cicdFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab-ci.yml', 'HEAD');
|
|
282
|
+
const cicdContent = Buffer.from(cicdFile.content, 'base64').toString('utf-8');
|
|
283
|
+
|
|
284
|
+
// Check for SAST
|
|
285
|
+
if (cicdContent.includes('sast') || cicdContent.includes('SAST')) {
|
|
286
|
+
checks.push(createCheck(
|
|
287
|
+
category,
|
|
288
|
+
'SAST Enabled',
|
|
289
|
+
'pass',
|
|
290
|
+
'Static Application Security Testing (SAST) is configured'
|
|
291
|
+
));
|
|
292
|
+
} else {
|
|
293
|
+
checks.push(createCheck(
|
|
294
|
+
category,
|
|
295
|
+
'SAST Enabled',
|
|
296
|
+
'warning',
|
|
297
|
+
'SAST is not configured',
|
|
298
|
+
'Add SAST template: include: - template: Security/SAST.gitlab-ci.yml'
|
|
299
|
+
));
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Check for dependency scanning
|
|
303
|
+
if (cicdContent.includes('dependency_scanning') || cicdContent.includes('Dependency-Scanning')) {
|
|
304
|
+
checks.push(createCheck(
|
|
305
|
+
category,
|
|
306
|
+
'Dependency Scanning Enabled',
|
|
307
|
+
'pass',
|
|
308
|
+
'Dependency scanning is configured'
|
|
309
|
+
));
|
|
310
|
+
} else {
|
|
311
|
+
checks.push(createCheck(
|
|
312
|
+
category,
|
|
313
|
+
'Dependency Scanning Enabled',
|
|
314
|
+
'warning',
|
|
315
|
+
'Dependency scanning is not configured',
|
|
316
|
+
'Add template: include: - template: Security/Dependency-Scanning.gitlab-ci.yml'
|
|
317
|
+
));
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Check for container scanning
|
|
321
|
+
if (cicdContent.includes('container_scanning') || cicdContent.includes('Container-Scanning')) {
|
|
322
|
+
checks.push(createCheck(
|
|
323
|
+
category,
|
|
324
|
+
'Container Scanning Enabled',
|
|
325
|
+
'pass',
|
|
326
|
+
'Container scanning is configured'
|
|
327
|
+
));
|
|
328
|
+
} else {
|
|
329
|
+
checks.push(createCheck(
|
|
330
|
+
category,
|
|
331
|
+
'Container Scanning Enabled',
|
|
332
|
+
'info',
|
|
333
|
+
'Container scanning is not configured',
|
|
334
|
+
'If using containers, add: include: - template: Security/Container-Scanning.gitlab-ci.yml'
|
|
335
|
+
));
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Check for secret detection
|
|
339
|
+
if (cicdContent.includes('secret_detection') || cicdContent.includes('Secret-Detection')) {
|
|
340
|
+
checks.push(createCheck(
|
|
341
|
+
category,
|
|
342
|
+
'Secret Detection Enabled',
|
|
343
|
+
'pass',
|
|
344
|
+
'Secret detection is configured'
|
|
345
|
+
));
|
|
346
|
+
} else {
|
|
347
|
+
checks.push(createCheck(
|
|
348
|
+
category,
|
|
349
|
+
'Secret Detection Enabled',
|
|
350
|
+
'warning',
|
|
351
|
+
'Secret detection is not configured',
|
|
352
|
+
'Add template: include: - template: Security/Secret-Detection.gitlab-ci.yml'
|
|
353
|
+
));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
} catch (error: any) {
|
|
357
|
+
checks.push(createCheck(
|
|
358
|
+
category,
|
|
359
|
+
'Security Scanning Check',
|
|
360
|
+
'fail',
|
|
361
|
+
`Failed to check CI/CD configuration: ${error.message}`
|
|
362
|
+
));
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return checks;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
async function auditDORAMetrics(projectId: string): Promise<ComplianceCheck[]> {
|
|
369
|
+
const checks: ComplianceCheck[] = [];
|
|
370
|
+
const category = 'DORA Metrics';
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
// Check if DORA metrics are available (GitLab Ultimate feature)
|
|
374
|
+
const project = await gitlab.Projects.show(projectId);
|
|
375
|
+
|
|
376
|
+
// Check for deployment frequency tracking
|
|
377
|
+
const cicdFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab-ci.yml', 'HEAD');
|
|
378
|
+
const cicdContent = Buffer.from(cicdFile.content, 'base64').toString('utf-8');
|
|
379
|
+
|
|
380
|
+
if (cicdContent.includes('environment:') || cicdContent.includes('deployment')) {
|
|
381
|
+
checks.push(createCheck(
|
|
382
|
+
category,
|
|
383
|
+
'Deployment Tracking Configured',
|
|
384
|
+
'pass',
|
|
385
|
+
'Environment deployments are configured for DORA tracking'
|
|
386
|
+
));
|
|
387
|
+
} else {
|
|
388
|
+
checks.push(createCheck(
|
|
389
|
+
category,
|
|
390
|
+
'Deployment Tracking Configured',
|
|
391
|
+
'warning',
|
|
392
|
+
'No deployment environments configured',
|
|
393
|
+
'Configure environments in .gitlab-ci.yml to track deployment frequency'
|
|
394
|
+
));
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Check for incident management
|
|
398
|
+
if (project.issues_enabled) {
|
|
399
|
+
checks.push(createCheck(
|
|
400
|
+
category,
|
|
401
|
+
'Issues Enabled',
|
|
402
|
+
'pass',
|
|
403
|
+
'Issues are enabled for incident tracking'
|
|
404
|
+
));
|
|
405
|
+
} else {
|
|
406
|
+
checks.push(createCheck(
|
|
407
|
+
category,
|
|
408
|
+
'Issues Enabled',
|
|
409
|
+
'warning',
|
|
410
|
+
'Issues are disabled',
|
|
411
|
+
'Enable issues: Settings > General > Visibility > Issues'
|
|
412
|
+
));
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
// Check for merge request metrics
|
|
416
|
+
if (project.merge_requests_enabled) {
|
|
417
|
+
checks.push(createCheck(
|
|
418
|
+
category,
|
|
419
|
+
'Merge Requests Enabled',
|
|
420
|
+
'pass',
|
|
421
|
+
'Merge requests enabled for lead time tracking'
|
|
422
|
+
));
|
|
423
|
+
} else {
|
|
424
|
+
checks.push(createCheck(
|
|
425
|
+
category,
|
|
426
|
+
'Merge Requests Enabled',
|
|
427
|
+
'fail',
|
|
428
|
+
'Merge requests are disabled',
|
|
429
|
+
'Enable merge requests: Settings > General > Visibility > Merge requests'
|
|
430
|
+
));
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
checks.push(createCheck(
|
|
434
|
+
category,
|
|
435
|
+
'DORA Metrics Dashboard',
|
|
436
|
+
'info',
|
|
437
|
+
'View DORA metrics: Analytics > CI/CD Analytics',
|
|
438
|
+
'DORA metrics require GitLab Ultimate'
|
|
439
|
+
));
|
|
440
|
+
|
|
441
|
+
} catch (error: any) {
|
|
442
|
+
checks.push(createCheck(
|
|
443
|
+
category,
|
|
444
|
+
'DORA Metrics Check',
|
|
445
|
+
'info',
|
|
446
|
+
`Could not fully verify DORA metrics setup: ${error.message}`,
|
|
447
|
+
'DORA metrics are a GitLab Ultimate feature'
|
|
448
|
+
));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
return checks;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
async function auditSecurityPolicies(projectId: string): Promise<ComplianceCheck[]> {
|
|
455
|
+
const checks: ComplianceCheck[] = [];
|
|
456
|
+
const category = 'Security Policies';
|
|
457
|
+
|
|
458
|
+
try {
|
|
459
|
+
const project = await gitlab.Projects.show(projectId);
|
|
460
|
+
|
|
461
|
+
// Check for security policies project (modern approach)
|
|
462
|
+
// Security policies are stored in a separate project linked via .gitlab/security-policies/policy.yml
|
|
463
|
+
try {
|
|
464
|
+
const policyFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab/security-policies/policy.yml', 'HEAD');
|
|
465
|
+
const policyContent = Buffer.from(policyFile.content, 'base64').toString('utf-8');
|
|
466
|
+
|
|
467
|
+
// Check for scan execution policies
|
|
468
|
+
if (policyContent.includes('scan_execution_policy:')) {
|
|
469
|
+
checks.push(createCheck(
|
|
470
|
+
category,
|
|
471
|
+
'Scan Execution Policies',
|
|
472
|
+
'pass',
|
|
473
|
+
'Scan execution policies are defined to enforce security scans'
|
|
474
|
+
));
|
|
475
|
+
|
|
476
|
+
// Check specific scan types
|
|
477
|
+
if (policyContent.includes('scan: sast')) {
|
|
478
|
+
checks.push(createCheck(category, 'SAST Policy', 'pass', 'SAST enforcement policy configured'));
|
|
479
|
+
}
|
|
480
|
+
if (policyContent.includes('scan: secret_detection')) {
|
|
481
|
+
checks.push(createCheck(category, 'Secret Detection Policy', 'pass', 'Secret detection enforcement policy configured'));
|
|
482
|
+
}
|
|
483
|
+
if (policyContent.includes('scan: dependency_scanning')) {
|
|
484
|
+
checks.push(createCheck(category, 'Dependency Scanning Policy', 'pass', 'Dependency scanning enforcement policy configured'));
|
|
485
|
+
}
|
|
486
|
+
if (policyContent.includes('scan: container_scanning')) {
|
|
487
|
+
checks.push(createCheck(category, 'Container Scanning Policy', 'pass', 'Container scanning enforcement policy configured'));
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
checks.push(createCheck(
|
|
491
|
+
category,
|
|
492
|
+
'Scan Execution Policies',
|
|
493
|
+
'warning',
|
|
494
|
+
'No scan execution policies found',
|
|
495
|
+
'Add scan_execution_policy to enforce security scans: Secure > Policies > New policy'
|
|
496
|
+
));
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Check for scan result policies (approval gates)
|
|
500
|
+
if (policyContent.includes('scan_result_policy:')) {
|
|
501
|
+
checks.push(createCheck(
|
|
502
|
+
category,
|
|
503
|
+
'Scan Result Policies',
|
|
504
|
+
'pass',
|
|
505
|
+
'Scan result policies are defined to block vulnerable code'
|
|
506
|
+
));
|
|
507
|
+
|
|
508
|
+
if (policyContent.includes('vulnerabilities_allowed: 0')) {
|
|
509
|
+
checks.push(createCheck(
|
|
510
|
+
category,
|
|
511
|
+
'Zero Vulnerability Policy',
|
|
512
|
+
'pass',
|
|
513
|
+
'Zero-tolerance vulnerability policy configured for critical findings'
|
|
514
|
+
));
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
checks.push(createCheck(
|
|
518
|
+
category,
|
|
519
|
+
'Scan Result Policies',
|
|
520
|
+
'warning',
|
|
521
|
+
'No scan result policies found',
|
|
522
|
+
'Add scan_result_policy to require approvals for vulnerabilities'
|
|
523
|
+
));
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Check for pipeline execution policies
|
|
527
|
+
if (policyContent.includes('pipeline_execution_policy:')) {
|
|
528
|
+
checks.push(createCheck(
|
|
529
|
+
category,
|
|
530
|
+
'Pipeline Execution Policies',
|
|
531
|
+
'pass',
|
|
532
|
+
'Pipeline execution policies inject compliance jobs'
|
|
533
|
+
));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
} catch {
|
|
537
|
+
// Check if linked to group security policies project
|
|
538
|
+
checks.push(createCheck(
|
|
539
|
+
category,
|
|
540
|
+
'Security Policies',
|
|
541
|
+
'info',
|
|
542
|
+
'No local security policies found - may be inherited from group',
|
|
543
|
+
'Configure policies: Secure > Policies or link to group security-policies project'
|
|
544
|
+
));
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
// Check for merge trains (still relevant)
|
|
548
|
+
if (project.merge_trains_enabled) {
|
|
549
|
+
checks.push(createCheck(
|
|
550
|
+
category,
|
|
551
|
+
'Merge Trains Enabled',
|
|
552
|
+
'pass',
|
|
553
|
+
'Merge trains are enabled for better pipeline efficiency'
|
|
554
|
+
));
|
|
555
|
+
} else {
|
|
556
|
+
checks.push(createCheck(
|
|
557
|
+
category,
|
|
558
|
+
'Merge Trains Enabled',
|
|
559
|
+
'info',
|
|
560
|
+
'Merge trains are not enabled',
|
|
561
|
+
'Enable: Settings > Merge requests > Merge trains (Premium/Ultimate)'
|
|
562
|
+
));
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Check for code owners
|
|
566
|
+
try {
|
|
567
|
+
await gitlab.RepositoryFiles.show(projectId, 'CODEOWNERS', 'HEAD');
|
|
568
|
+
checks.push(createCheck(
|
|
569
|
+
category,
|
|
570
|
+
'Code Owners Defined',
|
|
571
|
+
'pass',
|
|
572
|
+
'CODEOWNERS file exists for code review requirements'
|
|
573
|
+
));
|
|
574
|
+
} catch {
|
|
575
|
+
checks.push(createCheck(
|
|
576
|
+
category,
|
|
577
|
+
'Code Owners Defined',
|
|
578
|
+
'info',
|
|
579
|
+
'No CODEOWNERS file found',
|
|
580
|
+
'Create CODEOWNERS file to define code ownership'
|
|
581
|
+
));
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
} catch (error: any) {
|
|
585
|
+
checks.push(createCheck(
|
|
586
|
+
category,
|
|
587
|
+
'Security Policies Check',
|
|
588
|
+
'info',
|
|
589
|
+
`Could not check security policies: ${error.message}`,
|
|
590
|
+
'Security policies require GitLab Ultimate'
|
|
591
|
+
));
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
return checks;
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
async function auditPipelineConfiguration(projectId: string): Promise<ComplianceCheck[]> {
|
|
598
|
+
const checks: ComplianceCheck[] = [];
|
|
599
|
+
const category = 'Pipeline Configuration';
|
|
600
|
+
|
|
601
|
+
try {
|
|
602
|
+
// Check for CI/CD configuration
|
|
603
|
+
const cicdFile = await gitlab.RepositoryFiles.show(projectId, '.gitlab-ci.yml', 'HEAD');
|
|
604
|
+
checks.push(createCheck(
|
|
605
|
+
category,
|
|
606
|
+
'CI/CD Configuration',
|
|
607
|
+
'pass',
|
|
608
|
+
'.gitlab-ci.yml file exists and is configured'
|
|
609
|
+
));
|
|
610
|
+
|
|
611
|
+
// Get recent pipelines
|
|
612
|
+
const pipelines = await gitlab.Pipelines.all(projectId, { per_page: 10 });
|
|
613
|
+
if (pipelines && pipelines.length > 0) {
|
|
614
|
+
const successCount = pipelines.filter((p: any) => p.status === 'success').length;
|
|
615
|
+
const failCount = pipelines.filter((p: any) => p.status === 'failed').length;
|
|
616
|
+
|
|
617
|
+
checks.push(createCheck(
|
|
618
|
+
category,
|
|
619
|
+
'Pipeline Activity',
|
|
620
|
+
'pass',
|
|
621
|
+
`Recent pipelines: ${successCount} passed, ${failCount} failed out of last ${pipelines.length}`
|
|
622
|
+
));
|
|
623
|
+
|
|
624
|
+
const successRate = (successCount / pipelines.length) * 100;
|
|
625
|
+
if (successRate >= 80) {
|
|
626
|
+
checks.push(createCheck(
|
|
627
|
+
category,
|
|
628
|
+
'Pipeline Success Rate',
|
|
629
|
+
'pass',
|
|
630
|
+
`${successRate.toFixed(1)}% success rate`
|
|
631
|
+
));
|
|
632
|
+
} else if (successRate >= 60) {
|
|
633
|
+
checks.push(createCheck(
|
|
634
|
+
category,
|
|
635
|
+
'Pipeline Success Rate',
|
|
636
|
+
'warning',
|
|
637
|
+
`${successRate.toFixed(1)}% success rate - consider improving pipeline stability`
|
|
638
|
+
));
|
|
639
|
+
} else {
|
|
640
|
+
checks.push(createCheck(
|
|
641
|
+
category,
|
|
642
|
+
'Pipeline Success Rate',
|
|
643
|
+
'fail',
|
|
644
|
+
`${successRate.toFixed(1)}% success rate - pipeline needs attention`,
|
|
645
|
+
'Review failing jobs and fix underlying issues'
|
|
646
|
+
));
|
|
647
|
+
}
|
|
648
|
+
} else {
|
|
649
|
+
checks.push(createCheck(
|
|
650
|
+
category,
|
|
651
|
+
'Pipeline Activity',
|
|
652
|
+
'warning',
|
|
653
|
+
'No recent pipeline runs found',
|
|
654
|
+
'Ensure pipelines are running on commits'
|
|
655
|
+
));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
} catch (error: any) {
|
|
659
|
+
checks.push(createCheck(
|
|
660
|
+
category,
|
|
661
|
+
'Pipeline Configuration Check',
|
|
662
|
+
'fail',
|
|
663
|
+
`Failed to check pipeline configuration: ${error.message}`
|
|
664
|
+
));
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
return checks;
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
// Main audit function
|
|
671
|
+
async function runAudit(projectId: string): Promise<AuditReport> {
|
|
672
|
+
console.log(`\nš Running GitLab Ultimate Compliance Audit\n`);
|
|
673
|
+
console.log(`Project: ${projectId}`);
|
|
674
|
+
console.log(`GitLab Host: ${GITLAB_HOST}`);
|
|
675
|
+
console.log(`Token: ${TOKEN.substring(0, 10)}...\n`);
|
|
676
|
+
|
|
677
|
+
// Get project info
|
|
678
|
+
const project = await gitlab.Projects.show(projectId);
|
|
679
|
+
console.log(`Project Name: ${project.name}`);
|
|
680
|
+
console.log(`Project Path: ${project.path_with_namespace}\n`);
|
|
681
|
+
|
|
682
|
+
const checks: ComplianceCheck[] = [];
|
|
683
|
+
|
|
684
|
+
// Run all audit checks
|
|
685
|
+
console.log('Running audit checks...\n');
|
|
686
|
+
|
|
687
|
+
checks.push(...await auditProtectedBranches(projectId));
|
|
688
|
+
checks.push(...await auditMergeRequestApprovals(projectId));
|
|
689
|
+
checks.push(...await auditSecurityScanning(projectId));
|
|
690
|
+
checks.push(...await auditDORAMetrics(projectId));
|
|
691
|
+
checks.push(...await auditSecurityPolicies(projectId));
|
|
692
|
+
checks.push(...await auditPipelineConfiguration(projectId));
|
|
693
|
+
|
|
694
|
+
// Calculate summary
|
|
695
|
+
const summary = {
|
|
696
|
+
total: checks.length,
|
|
697
|
+
passed: checks.filter(c => c.status === 'pass').length,
|
|
698
|
+
failed: checks.filter(c => c.status === 'fail').length,
|
|
699
|
+
warnings: checks.filter(c => c.status === 'warning').length,
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
// Determine overall status
|
|
703
|
+
let overallStatus: 'compliant' | 'non-compliant' | 'partial';
|
|
704
|
+
if (summary.failed === 0 && summary.warnings === 0) {
|
|
705
|
+
overallStatus = 'compliant';
|
|
706
|
+
} else if (summary.failed > 0) {
|
|
707
|
+
overallStatus = 'non-compliant';
|
|
708
|
+
} else {
|
|
709
|
+
overallStatus = 'partial';
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
return {
|
|
713
|
+
projectId: project.path_with_namespace,
|
|
714
|
+
projectName: project.name,
|
|
715
|
+
auditDate: new Date().toISOString(),
|
|
716
|
+
overallStatus,
|
|
717
|
+
checks,
|
|
718
|
+
summary,
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// Display functions
|
|
723
|
+
function displayReport(report: AuditReport): void {
|
|
724
|
+
console.log('\n' + '='.repeat(80));
|
|
725
|
+
console.log('COMPLIANCE AUDIT REPORT');
|
|
726
|
+
console.log('='.repeat(80));
|
|
727
|
+
console.log(`Project: ${report.projectName} (${report.projectId})`);
|
|
728
|
+
console.log(`Date: ${new Date(report.auditDate).toLocaleString()}`);
|
|
729
|
+
console.log(`Overall Status: ${report.overallStatus.toUpperCase()}`);
|
|
730
|
+
console.log('='.repeat(80));
|
|
731
|
+
|
|
732
|
+
// Group checks by category
|
|
733
|
+
const categories = [...new Set(report.checks.map(c => c.category))];
|
|
734
|
+
|
|
735
|
+
for (const category of categories) {
|
|
736
|
+
console.log(`\nš ${category}`);
|
|
737
|
+
console.log('-'.repeat(80));
|
|
738
|
+
|
|
739
|
+
const categoryChecks = report.checks.filter(c => c.category === category);
|
|
740
|
+
for (const check of categoryChecks) {
|
|
741
|
+
console.log(`${getStatusIcon(check.status)} ${check.check}`);
|
|
742
|
+
console.log(` ${check.message}`);
|
|
743
|
+
if (check.remediation) {
|
|
744
|
+
console.log(` š” ${check.remediation}`);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
console.log('\n' + '='.repeat(80));
|
|
750
|
+
console.log('SUMMARY');
|
|
751
|
+
console.log('='.repeat(80));
|
|
752
|
+
console.log(`Total Checks: ${report.summary.total}`);
|
|
753
|
+
console.log(`ā
Passed: ${report.summary.passed}`);
|
|
754
|
+
console.log(`ā Failed: ${report.summary.failed}`);
|
|
755
|
+
console.log(`ā ļø Warnings: ${report.summary.warnings}`);
|
|
756
|
+
console.log(`ā¹ļø Info: ${report.summary.total - report.summary.passed - report.summary.failed - report.summary.warnings}`);
|
|
757
|
+
console.log('='.repeat(80) + '\n');
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function saveReport(report: AuditReport, filename: string): void {
|
|
761
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
762
|
+
const outputFile = filename || `compliance-audit-${report.projectId.replace('/', '-')}-${timestamp}.json`;
|
|
763
|
+
|
|
764
|
+
writeFileSync(outputFile, JSON.stringify(report, null, 2));
|
|
765
|
+
console.log(`\nš Report saved to: ${outputFile}`);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Main execution
|
|
769
|
+
async function main() {
|
|
770
|
+
const projectId = process.argv[2] || DEFAULT_PROJECT;
|
|
771
|
+
const outputFile = process.argv[3];
|
|
772
|
+
|
|
773
|
+
try {
|
|
774
|
+
const report = await runAudit(projectId);
|
|
775
|
+
displayReport(report);
|
|
776
|
+
saveReport(report, outputFile);
|
|
777
|
+
|
|
778
|
+
// Exit with appropriate code
|
|
779
|
+
if (report.overallStatus === 'non-compliant') {
|
|
780
|
+
console.log('ā ļø Project has compliance issues that need attention\n');
|
|
781
|
+
process.exit(1);
|
|
782
|
+
} else if (report.overallStatus === 'partial') {
|
|
783
|
+
console.log('ā ļø Project is partially compliant - review warnings\n');
|
|
784
|
+
process.exit(0);
|
|
785
|
+
} else {
|
|
786
|
+
console.log('ā
Project is fully compliant!\n');
|
|
787
|
+
process.exit(0);
|
|
788
|
+
}
|
|
789
|
+
} catch (error: any) {
|
|
790
|
+
console.error('\nā Audit failed:', error.message);
|
|
791
|
+
console.error(error.stack);
|
|
792
|
+
process.exit(1);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
main();
|