fix
Some checks failed
Dev CI / Backend — Lint (push) Successful in 1m4s
Dev CI / Security gate (push) Failing after 1m36s
Dev CI / Backend — Unit Tests (push) Successful in 1m7s
Dev CI / Frontend — Lint & Type-check (push) Successful in 1m51s
Dev CI / Frontend — Unit Tests (push) Successful in 1m22s
Dev CI / Notify Failure (push) Has been skipped
Some checks failed
Dev CI / Backend — Lint (push) Successful in 1m4s
Dev CI / Security gate (push) Failing after 1m36s
Dev CI / Backend — Unit Tests (push) Successful in 1m7s
Dev CI / Frontend — Lint & Type-check (push) Successful in 1m51s
Dev CI / Frontend — Unit Tests (push) Successful in 1m22s
Dev CI / Notify Failure (push) Has been skipped
This commit is contained in:
parent
e055af9afe
commit
5c59ef044b
@ -4,7 +4,11 @@ runs:
|
|||||||
using: composite
|
using: composite
|
||||||
steps:
|
steps:
|
||||||
- uses: ./.gitea/actions/setup-node
|
- uses: ./.gitea/actions/setup-node
|
||||||
- uses: ./.gitea/actions/setup-trivy
|
- name: Install Trivy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
trivy_bin=$(bash scripts/ci/install-tool.sh trivy)
|
||||||
|
"$trivy_bin" --version
|
||||||
- name: Validate workflows and deployment checks
|
- name: Validate workflows and deployment checks
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
@ -13,6 +17,10 @@ runs:
|
|||||||
- name: Audit dependencies, secrets and infrastructure
|
- name: Audit dependencies, secrets and infrastructure
|
||||||
shell: bash
|
shell: bash
|
||||||
run: bash scripts/ci/security-audit.sh
|
run: bash scripts/ci/security-audit.sh
|
||||||
|
- name: Show security results
|
||||||
|
if: always()
|
||||||
|
shell: bash
|
||||||
|
run: python3 scripts/ci/summarize-security.py
|
||||||
- name: Save security reports on Gitea
|
- name: Save security reports on Gitea
|
||||||
if: always()
|
if: always()
|
||||||
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3, Gitea artifact protocol
|
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3, Gitea artifact protocol
|
||||||
|
|||||||
@ -40,7 +40,32 @@ jobs:
|
|||||||
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: ./.gitea/actions/security
|
- uses: ./.gitea/actions/setup-node
|
||||||
|
- name: Install Trivy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
trivy_bin=$(bash scripts/ci/install-tool.sh trivy)
|
||||||
|
"$trivy_bin" --version
|
||||||
|
- name: Validate workflows and deployment checks
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
actionlint_bin=$(bash scripts/ci/install-tool.sh actionlint)
|
||||||
|
ACTIONLINT_BIN="$actionlint_bin" bash scripts/ci/validate-workflows.sh
|
||||||
|
- name: Audit dependencies, secrets and infrastructure
|
||||||
|
shell: bash
|
||||||
|
run: bash scripts/ci/security-audit.sh
|
||||||
|
- name: Show security results
|
||||||
|
if: always()
|
||||||
|
shell: bash
|
||||||
|
run: python3 scripts/ci/summarize-security.py
|
||||||
|
- name: Save security reports on Gitea
|
||||||
|
if: always()
|
||||||
|
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3, Gitea artifact protocol
|
||||||
|
with:
|
||||||
|
name: security-reports
|
||||||
|
path: ${{ runner.temp }}/security-reports/*.json
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
# ═══ 1. Qualité ══════════════════════════════════════════════════════════
|
# ═══ 1. Qualité ══════════════════════════════════════════════════════════
|
||||||
backend-quality:
|
backend-quality:
|
||||||
|
|||||||
@ -28,7 +28,32 @@ jobs:
|
|||||||
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: ./.gitea/actions/security
|
- uses: ./.gitea/actions/setup-node
|
||||||
|
- name: Install Trivy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
trivy_bin=$(bash scripts/ci/install-tool.sh trivy)
|
||||||
|
"$trivy_bin" --version
|
||||||
|
- name: Validate workflows and deployment checks
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
actionlint_bin=$(bash scripts/ci/install-tool.sh actionlint)
|
||||||
|
ACTIONLINT_BIN="$actionlint_bin" bash scripts/ci/validate-workflows.sh
|
||||||
|
- name: Audit dependencies, secrets and infrastructure
|
||||||
|
shell: bash
|
||||||
|
run: bash scripts/ci/security-audit.sh
|
||||||
|
- name: Show security results
|
||||||
|
if: always()
|
||||||
|
shell: bash
|
||||||
|
run: python3 scripts/ci/summarize-security.py
|
||||||
|
- name: Save security reports on Gitea
|
||||||
|
if: always()
|
||||||
|
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3, Gitea artifact protocol
|
||||||
|
with:
|
||||||
|
name: security-reports
|
||||||
|
path: ${{ runner.temp }}/security-reports/*.json
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
# ── 1. Lint ─────────────────────────────────────────────────────────
|
# ── 1. Lint ─────────────────────────────────────────────────────────
|
||||||
backend-quality:
|
backend-quality:
|
||||||
|
|||||||
@ -14,7 +14,32 @@ jobs:
|
|||||||
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: ./.gitea/actions/security
|
- uses: ./.gitea/actions/setup-node
|
||||||
|
- name: Install Trivy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
trivy_bin=$(bash scripts/ci/install-tool.sh trivy)
|
||||||
|
"$trivy_bin" --version
|
||||||
|
- name: Validate workflows and deployment checks
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
actionlint_bin=$(bash scripts/ci/install-tool.sh actionlint)
|
||||||
|
ACTIONLINT_BIN="$actionlint_bin" bash scripts/ci/validate-workflows.sh
|
||||||
|
- name: Audit dependencies, secrets and infrastructure
|
||||||
|
shell: bash
|
||||||
|
run: bash scripts/ci/security-audit.sh
|
||||||
|
- name: Show security results
|
||||||
|
if: always()
|
||||||
|
shell: bash
|
||||||
|
run: python3 scripts/ci/summarize-security.py
|
||||||
|
- name: Save security reports on Gitea
|
||||||
|
if: always()
|
||||||
|
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3, Gitea artifact protocol
|
||||||
|
with:
|
||||||
|
name: security-reports
|
||||||
|
path: ${{ runner.temp }}/security-reports/*.json
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
backend-quality:
|
backend-quality:
|
||||||
name: Backend — Lint
|
name: Backend — Lint
|
||||||
|
|||||||
@ -16,7 +16,32 @@ jobs:
|
|||||||
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
- uses: https://github.com/actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
|
||||||
with:
|
with:
|
||||||
persist-credentials: false
|
persist-credentials: false
|
||||||
- uses: ./.gitea/actions/security
|
- uses: ./.gitea/actions/setup-node
|
||||||
|
- name: Install Trivy
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
trivy_bin=$(bash scripts/ci/install-tool.sh trivy)
|
||||||
|
"$trivy_bin" --version
|
||||||
|
- name: Validate workflows and deployment checks
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
actionlint_bin=$(bash scripts/ci/install-tool.sh actionlint)
|
||||||
|
ACTIONLINT_BIN="$actionlint_bin" bash scripts/ci/validate-workflows.sh
|
||||||
|
- name: Audit dependencies, secrets and infrastructure
|
||||||
|
shell: bash
|
||||||
|
run: bash scripts/ci/security-audit.sh
|
||||||
|
- name: Show security results
|
||||||
|
if: always()
|
||||||
|
shell: bash
|
||||||
|
run: python3 scripts/ci/summarize-security.py
|
||||||
|
- name: Save security reports on Gitea
|
||||||
|
if: always()
|
||||||
|
uses: https://github.com/actions/upload-artifact@ff15f0306b3f739f7b6fd43fb5d26cd321bd4de5 # v3, Gitea artifact protocol
|
||||||
|
with:
|
||||||
|
name: security-reports
|
||||||
|
path: ${{ runner.temp }}/security-reports/*.json
|
||||||
|
retention-days: 7
|
||||||
|
if-no-files-found: error
|
||||||
|
|
||||||
backend-quality:
|
backend-quality:
|
||||||
name: Backend — Lint
|
name: Backend — Lint
|
||||||
|
|||||||
@ -11,7 +11,7 @@ import { IsEmail, IsOptional, IsString, MaxLength } from 'class-validator';
|
|||||||
export class DeleteAccountDto {
|
export class DeleteAccountDto {
|
||||||
@ApiProperty({
|
@ApiProperty({
|
||||||
example: 'personne@example.com',
|
example: 'personne@example.com',
|
||||||
description: "Adresse du compte, ressaisie pour confirmer un acte irréversible",
|
description: 'Adresse du compte, ressaisie pour confirmer un acte irréversible',
|
||||||
})
|
})
|
||||||
@IsEmail({}, { message: 'Une adresse email valide est requise pour confirmer.' })
|
@IsEmail({}, { message: 'Une adresse email valide est requise pour confirmer.' })
|
||||||
confirmEmail: string;
|
confirmEmail: string;
|
||||||
|
|||||||
@ -54,7 +54,9 @@ function buildService(options: { user?: UserOrmEntity | null } = {}) {
|
|||||||
} as unknown as EntityManager;
|
} as unknown as EntityManager;
|
||||||
|
|
||||||
const dataSource = {
|
const dataSource = {
|
||||||
transaction: jest.fn(async (callback: (m: EntityManager) => Promise<void>) => callback(manager)),
|
transaction: jest.fn(async (callback: (m: EntityManager) => Promise<void>) =>
|
||||||
|
callback(manager)
|
||||||
|
),
|
||||||
query: jest.fn(async (sql: string, parameters: unknown[]) => {
|
query: jest.fn(async (sql: string, parameters: unknown[]) => {
|
||||||
executed.push({ sql, parameters });
|
executed.push({ sql, parameters });
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
@ -208,7 +208,9 @@ export class GDPRService {
|
|||||||
const user = await this.userRepository.findOne({ where: { id: userId } });
|
const user = await this.userRepository.findOne({ where: { id: userId } });
|
||||||
if (!user) throw new NotFoundException('User not found');
|
if (!user) throw new NotFoundException('User not found');
|
||||||
|
|
||||||
this.logger.warn(`GDPR erasure starting for user ${userId} — reason: ${reason ?? 'unspecified'}`);
|
this.logger.warn(
|
||||||
|
`GDPR erasure starting for user ${userId} — reason: ${reason ?? 'unspecified'}`
|
||||||
|
);
|
||||||
|
|
||||||
const deleted: Record<string, number> = {};
|
const deleted: Record<string, number> = {};
|
||||||
const anonymised: Record<string, number> = {};
|
const anonymised: Record<string, number> = {};
|
||||||
@ -228,7 +230,9 @@ export class GDPRService {
|
|||||||
userId,
|
userId,
|
||||||
]);
|
]);
|
||||||
deleted.apiKeys = await rows('DELETE FROM api_keys WHERE user_id = $1', [userId]);
|
deleted.apiKeys = await rows('DELETE FROM api_keys WHERE user_id = $1', [userId]);
|
||||||
deleted.cookieConsent = await rows('DELETE FROM cookie_consents WHERE user_id = $1', [userId]);
|
deleted.cookieConsent = await rows('DELETE FROM cookie_consents WHERE user_id = $1', [
|
||||||
|
userId,
|
||||||
|
]);
|
||||||
deleted.passwordResetTokens = await rows(
|
deleted.passwordResetTokens = await rows(
|
||||||
'DELETE FROM password_reset_tokens WHERE user_id = $1',
|
'DELETE FROM password_reset_tokens WHERE user_id = $1',
|
||||||
[userId]
|
[userId]
|
||||||
@ -361,7 +365,9 @@ export class GDPRService {
|
|||||||
|
|
||||||
const next: UpdateConsentDto = {
|
const next: UpdateConsentDto = {
|
||||||
essential: true,
|
essential: true,
|
||||||
functional: consentType ? consentType !== 'functional' && (current?.functional ?? false) : false,
|
functional: consentType
|
||||||
|
? consentType !== 'functional' && (current?.functional ?? false)
|
||||||
|
: false,
|
||||||
analytics: consentType ? consentType !== 'analytics' && (current?.analytics ?? false) : false,
|
analytics: consentType ? consentType !== 'analytics' && (current?.analytics ?? false) : false,
|
||||||
marketing: consentType ? consentType !== 'marketing' && (current?.marketing ?? false) : false,
|
marketing: consentType ? consentType !== 'marketing' && (current?.marketing ?? false) : false,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -49,7 +49,7 @@ describe('RetentionService', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("épargne les traces de traitement des demandes de droits", async () => {
|
it('épargne les traces de traitement des demandes de droits', async () => {
|
||||||
const { service, executed } = buildService();
|
const { service, executed } = buildService();
|
||||||
|
|
||||||
await service.purge();
|
await service.purge();
|
||||||
|
|||||||
@ -18,8 +18,5 @@ export interface ShipmentCounterPort {
|
|||||||
* an organization in a given year. Unpaid drafts (QUOTE), rejected and
|
* an organization in a given year. Unpaid drafts (QUOTE), rejected and
|
||||||
* cancelled bookings are excluded.
|
* cancelled bookings are excluded.
|
||||||
*/
|
*/
|
||||||
countPaidShipmentsForOrganizationInYear(
|
countPaidShipmentsForOrganizationInYear(organizationId: string, year: number): Promise<number>;
|
||||||
organizationId: string,
|
|
||||||
year: number
|
|
||||||
): Promise<number>;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
import { Injectable, Logger } from '@nestjs/common';
|
import { Injectable, Logger } from '@nestjs/common';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
import {
|
import { CsvRateConfigOrmEntity, CsvRateDirection } from '../entities/csv-rate-config.orm-entity';
|
||||||
CsvRateConfigOrmEntity,
|
|
||||||
CsvRateDirection,
|
|
||||||
} from '../entities/csv-rate-config.orm-entity';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* CSV Rate Config Repository Port
|
* CSV Rate Config Repository Port
|
||||||
|
|||||||
@ -142,7 +142,7 @@ ces configurations ne signale plus ce secret après modification.
|
|||||||
|
|
||||||
## Validation
|
## Validation
|
||||||
|
|
||||||
Les 25 tests offline de promotion et santé passent : arbre différent, marqueurs
|
Les 27 tests offline de promotion et santé passent : arbre différent, marqueurs
|
||||||
manquants, panne du registre, digest invalide, mauvaise branche, saisie hostile et
|
manquants, panne du registre, digest invalide, mauvaise branche, saisie hostile et
|
||||||
échec HTTP sont refusés. Les tests vérifient aussi que les échecs et délais des
|
échec HTTP sont refusés. Les tests vérifient aussi que les échecs et délais des
|
||||||
audits restent bloquants et que le rapport masque les valeurs de secrets. Actionlint valide la syntaxe après normalisation des URLs
|
audits restent bloquants et que le rapport masque les valeurs de secrets. Actionlint valide la syntaxe après normalisation des URLs
|
||||||
@ -172,3 +172,20 @@ sur les runners de l'instance.
|
|||||||
|
|
||||||
Références : [différences Gitea 1.22](https://docs.gitea.com/1.22/usage/actions/comparison/),
|
Références : [différences Gitea 1.22](https://docs.gitea.com/1.22/usage/actions/comparison/),
|
||||||
[Renovate sur Gitea](https://docs.renovatebot.com/modules/platform/gitea/).
|
[Renovate sur Gitea](https://docs.renovatebot.com/modules/platform/gitea/).
|
||||||
|
|
||||||
|
## Diagnostic du run Gitea 146
|
||||||
|
|
||||||
|
Le [journal complet du job sécurité](https://gitea.ops.xpeditis.com/David/xpeditis2.0/actions/runs/146/jobs/0/logs)
|
||||||
|
confirme que Node 22 et Trivy démarrent, que les validations passent et que
|
||||||
|
l'artefact `security-reports` est bien téléversé. L'affichage de l'action composite
|
||||||
|
s'arrête pourtant visuellement au lancement de Trivy. Les quatre workflows
|
||||||
|
exposent désormais installation, validation, audit, résumé et téléversement comme
|
||||||
|
étapes distinctes. Le résumé s'exécute même si l'audit échoue, sans afficher de
|
||||||
|
valeurs de secrets. Un rapport absent ou invalide est signalé comme indisponible.
|
||||||
|
|
||||||
|
Ce run échoue réellement sur les audits : 50 vulnérabilités élevées backend,
|
||||||
|
13 élevées et une critique frontend, quatre détections de secrets et douze
|
||||||
|
alertes d'infrastructure. Ces nombres décrivent ce run, pas un audit futur.
|
||||||
|
Les seuils restent bloquants ; cette correction d'affichage ne corrige pas les
|
||||||
|
vulnérabilités. Les douze erreurs Prettier du job backend ont été corrigées et
|
||||||
|
le lint sans correction automatique passe localement.
|
||||||
|
|||||||
45
scripts/ci/summarize-security.py
Normal file
45
scripts/ci/summarize-security.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
"""Print audit counts without exposing secret matches or source snippets."""
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def summarize(reports):
|
||||||
|
incomplete = False
|
||||||
|
|
||||||
|
def read(name):
|
||||||
|
nonlocal incomplete
|
||||||
|
try:
|
||||||
|
report = json.loads((reports / name).read_text())
|
||||||
|
if not isinstance(report, dict) or report.get('error'):
|
||||||
|
raise ValueError('Invalid audit report')
|
||||||
|
return report
|
||||||
|
except (OSError, ValueError):
|
||||||
|
print(f'{name}: report missing, invalid or scanner error; inspect the audit step.')
|
||||||
|
incomplete = True
|
||||||
|
return {}
|
||||||
|
|
||||||
|
for project in ('root', 'backend', 'frontend', 'log-exporter'):
|
||||||
|
report = read(f'npm-audit-{project}.json')
|
||||||
|
counts = report.get('metadata', {}).get('vulnerabilities', {})
|
||||||
|
if 'high' not in counts or 'critical' not in counts:
|
||||||
|
print(f'{project}: dependency audit unavailable.')
|
||||||
|
incomplete = True
|
||||||
|
continue
|
||||||
|
print(f'{project}: {counts["high"]} high, {counts["critical"]} critical vulnerabilities.')
|
||||||
|
|
||||||
|
report = read('source-security.json')
|
||||||
|
if 'Results' not in report:
|
||||||
|
print('Source audit unavailable.')
|
||||||
|
incomplete = True
|
||||||
|
else:
|
||||||
|
results = report['Results'] or []
|
||||||
|
secrets = sum(len(result.get('Secrets') or []) for result in results)
|
||||||
|
misconfigs = sum(len(result.get('Misconfigurations') or []) for result in results)
|
||||||
|
print(f'Source: {secrets} secret findings, {misconfigs} infrastructure findings.')
|
||||||
|
print('Download the security-reports artifact for details. The audit step determines pass/fail.')
|
||||||
|
return int(incomplete)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
raise SystemExit(summarize(Path(os.environ['RUNNER_TEMP']) / 'security-reports'))
|
||||||
45
scripts/ci/test_security_summary.py
Normal file
45
scripts/ci/test_security_summary.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
"""Diagnostics must not leak scanner evidence or report missing audits as clean."""
|
||||||
|
import contextlib
|
||||||
|
import importlib.util
|
||||||
|
import io
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
'security_summary', Path(__file__).with_name('summarize-security.py'))
|
||||||
|
summary = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(summary)
|
||||||
|
|
||||||
|
|
||||||
|
class SecuritySummary(unittest.TestCase):
|
||||||
|
def test_real_findings_are_counted_without_printing_evidence(self):
|
||||||
|
with tempfile.TemporaryDirectory() as temp:
|
||||||
|
reports = Path(temp)
|
||||||
|
for project in ('root', 'backend', 'frontend', 'log-exporter'):
|
||||||
|
(reports / f'npm-audit-{project}.json').write_text(json.dumps({
|
||||||
|
'metadata': {'vulnerabilities': {'high': 2, 'critical': 1}}}))
|
||||||
|
(reports / 'source-security.json').write_text(json.dumps({'Results': [{
|
||||||
|
'Secrets': [{'Match': 'DO-NOT-PRINT', 'Code': 'PRIVATE-CODE'}],
|
||||||
|
'Misconfigurations': [{'Description': 'PRIVATE-DESCRIPTION'}]}]}))
|
||||||
|
output = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(output):
|
||||||
|
self.assertEqual(summary.summarize(reports), 0)
|
||||||
|
text = output.getvalue()
|
||||||
|
self.assertIn('2 high, 1 critical', text)
|
||||||
|
self.assertIn('1 secret findings, 1 infrastructure findings', text)
|
||||||
|
self.assertNotIn('DO-NOT-PRINT', text)
|
||||||
|
self.assertNotIn('PRIVATE-', text)
|
||||||
|
|
||||||
|
def test_missing_and_failed_audits_are_not_reported_as_clean(self):
|
||||||
|
with tempfile.TemporaryDirectory() as temp:
|
||||||
|
reports = Path(temp)
|
||||||
|
(reports / 'npm-audit-root.json').write_text('{invalid')
|
||||||
|
(reports / 'npm-audit-backend.json').write_text('{"error": {"code": "NETWORK"}}')
|
||||||
|
output = io.StringIO()
|
||||||
|
with contextlib.redirect_stdout(output):
|
||||||
|
self.assertEqual(summary.summarize(reports), 1)
|
||||||
|
self.assertIn('dependency audit unavailable', output.getvalue())
|
||||||
|
self.assertIn('Source audit unavailable', output.getvalue())
|
||||||
|
self.assertNotIn('0 high', output.getvalue())
|
||||||
Loading…
Reference in New Issue
Block a user