mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
SolidityScan API response schema validation (#2190)
* add schema validation to solidity scan report resource * report invalid API schema event to prometheus
This commit is contained in:
@@ -77,6 +77,7 @@ frontend:
|
||||
NEXT_PUBLIC_AD_BANNER_PROVIDER: slise
|
||||
NEXT_PUBLIC_DATA_AVAILABILITY_ENABLED: true
|
||||
NEXT_PUBLIC_NAVIGATION_HIGHLIGHTED_ROUTES: "['/apps']"
|
||||
PROMETHEUS_METRICS_ENABLED: true
|
||||
envFromSecret:
|
||||
NEXT_PUBLIC_SENTRY_DSN: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/NEXT_PUBLIC_SENTRY_DSN
|
||||
SENTRY_CSP_REPORT_URI: ref+vault://deployment-values/blockscout/dev/review?token_env=VAULT_TOKEN&address=https://vault.k8s.blockscout.com#/SENTRY_CSP_REPORT_URI
|
||||
|
||||
+3
-2
@@ -10,11 +10,12 @@ export default function buildUrl<R extends ResourceName>(
|
||||
resourceName: R,
|
||||
pathParams?: ResourcePathParams<R>,
|
||||
queryParams?: Record<string, string | Array<string> | number | boolean | null | undefined>,
|
||||
noProxy?: boolean,
|
||||
): string {
|
||||
const resource: ApiResource = RESOURCES[resourceName];
|
||||
const baseUrl = isNeedProxy() ? config.app.baseUrl : (resource.endpoint || config.api.endpoint);
|
||||
const baseUrl = !noProxy && isNeedProxy() ? config.app.baseUrl : (resource.endpoint || config.api.endpoint);
|
||||
const basePath = resource.basePath !== undefined ? resource.basePath : config.api.basePath;
|
||||
const path = isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
|
||||
const path = !noProxy && isNeedProxy() ? '/node-api/proxy' + basePath + resource.path : basePath + resource.path;
|
||||
const url = new URL(compile(path)(pathParams), baseUrl);
|
||||
|
||||
queryParams && Object.entries(queryParams).forEach(([ key, value ]) => {
|
||||
|
||||
@@ -57,7 +57,6 @@ import type { BackendVersionConfig } from 'types/api/configs';
|
||||
import type {
|
||||
SmartContract,
|
||||
SmartContractVerificationConfigRaw,
|
||||
SolidityscanReport,
|
||||
SmartContractSecurityAudits,
|
||||
} from 'types/api/contract';
|
||||
import type { VerifiedContractsResponse, VerifiedContractsFilters, VerifiedContractsCounters } from 'types/api/contracts';
|
||||
@@ -480,7 +479,7 @@ export const RESOURCES = {
|
||||
path: '/api/v2/smart-contracts/:hash/verification/via/:method',
|
||||
pathParams: [ 'hash' as const, 'method' as const ],
|
||||
},
|
||||
contract_solidityscan_report: {
|
||||
contract_solidity_scan_report: {
|
||||
path: '/api/v2/smart-contracts/:hash/solidityscan-report',
|
||||
pathParams: [ 'hash' as const ],
|
||||
},
|
||||
@@ -1038,7 +1037,7 @@ Q extends 'quick_search' ? Array<SearchResultItem> :
|
||||
Q extends 'search' ? SearchResult :
|
||||
Q extends 'search_check_redirect' ? SearchRedirectResult :
|
||||
Q extends 'contract' ? SmartContract :
|
||||
Q extends 'contract_solidityscan_report' ? SolidityscanReport :
|
||||
Q extends 'contract_solidity_scan_report' ? unknown :
|
||||
Q extends 'verified_contracts' ? VerifiedContractsResponse :
|
||||
Q extends 'verified_contracts_counters' ? VerifiedContractsCounters :
|
||||
Q extends 'visualize_sol2uml' ? visualizer.VisualizeResponse :
|
||||
|
||||
@@ -54,6 +54,7 @@ const OG_TYPE_DICT: Record<Route['pathname'], OGPageType> = {
|
||||
'/login': 'Regular page',
|
||||
'/sprite': 'Regular page',
|
||||
'/api/metrics': 'Regular page',
|
||||
'/api/monitoring/invalid-api-schema': 'Regular page',
|
||||
'/api/log': 'Regular page',
|
||||
'/api/media-type': 'Regular page',
|
||||
'/api/proxy': 'Regular page',
|
||||
|
||||
@@ -58,6 +58,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
|
||||
'/login': DEFAULT_TEMPLATE,
|
||||
'/sprite': DEFAULT_TEMPLATE,
|
||||
'/api/metrics': DEFAULT_TEMPLATE,
|
||||
'/api/monitoring/invalid-api-schema': DEFAULT_TEMPLATE,
|
||||
'/api/log': DEFAULT_TEMPLATE,
|
||||
'/api/media-type': DEFAULT_TEMPLATE,
|
||||
'/api/proxy': DEFAULT_TEMPLATE,
|
||||
|
||||
@@ -54,6 +54,7 @@ const TEMPLATE_MAP: Record<Route['pathname'], string> = {
|
||||
'/login': '%network_name% login',
|
||||
'/sprite': '%network_name% SVG sprite',
|
||||
'/api/metrics': '%network_name% node API prometheus metrics',
|
||||
'/api/monitoring/invalid-api-schema': '%network_name% node API prometheus metrics',
|
||||
'/api/log': '%network_name% node API request log',
|
||||
'/api/media-type': '%network_name% node API media type',
|
||||
'/api/proxy': '%network_name% node API proxy',
|
||||
|
||||
@@ -52,6 +52,7 @@ export const PAGE_TYPE_DICT: Record<Route['pathname'], string> = {
|
||||
'/login': 'Login',
|
||||
'/sprite': 'Sprite',
|
||||
'/api/metrics': 'Node API: Prometheus metrics',
|
||||
'/api/monitoring/invalid-api-schema': 'Node API: Prometheus metrics',
|
||||
'/api/log': 'Node API: Request log',
|
||||
'/api/media-type': 'Node API: Media type',
|
||||
'/api/proxy': 'Node API: Proxy',
|
||||
|
||||
@@ -8,6 +8,12 @@ const metrics = (() => {
|
||||
|
||||
promClient.register.clear();
|
||||
|
||||
const invalidApiSchema = new promClient.Counter({
|
||||
name: 'invalid_api_schema',
|
||||
help: 'Number of invalid external API schema events',
|
||||
labelNames: [ 'resource', 'url' ] as const,
|
||||
});
|
||||
|
||||
const socialPreviewBotRequests = new promClient.Counter({
|
||||
name: 'social_preview_bot_requests_total',
|
||||
help: 'Number of incoming requests from social preview bots',
|
||||
@@ -27,7 +33,7 @@ const metrics = (() => {
|
||||
buckets: [ 0.2, 0.5, 1, 3, 10 ],
|
||||
});
|
||||
|
||||
return { socialPreviewBotRequests, searchEngineBotRequests, apiRequestDuration };
|
||||
return { invalidApiSchema, socialPreviewBotRequests, searchEngineBotRequests, apiRequestDuration };
|
||||
})();
|
||||
|
||||
export default metrics;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import * as v from 'valibot';
|
||||
|
||||
export const SolidityScanIssueSeverityDistributionSchema = v.object({
|
||||
critical: v.number(),
|
||||
gas: v.number(),
|
||||
high: v.number(),
|
||||
informational: v.number(),
|
||||
low: v.number(),
|
||||
medium: v.number(),
|
||||
});
|
||||
|
||||
export const SolidityScanSchema = v.object({
|
||||
scan_report: v.object({
|
||||
contractname: v.string(),
|
||||
scan_status: v.string(),
|
||||
scan_summary: v.object({
|
||||
score_v2: v.string(),
|
||||
issue_severity_distribution: SolidityScanIssueSeverityDistributionSchema,
|
||||
}),
|
||||
scanner_reference_url: v.string(),
|
||||
}),
|
||||
});
|
||||
|
||||
export type SolidityScanReport = v.InferOutput<typeof SolidityScanSchema>;
|
||||
export type SolidityScanReportSeverityDistribution = v.InferOutput<typeof SolidityScanIssueSeverityDistributionSchema>;
|
||||
@@ -0,0 +1,51 @@
|
||||
import React from 'react';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import buildUrl from 'lib/api/buildUrl';
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import { SOLIDITY_SCAN_REPORT } from 'stubs/contract';
|
||||
|
||||
import { SolidityScanSchema } from './schema';
|
||||
|
||||
interface Params {
|
||||
hash: string;
|
||||
}
|
||||
|
||||
const RESOURCE_NAME = 'contract_solidity_scan_report';
|
||||
const ERROR_NAME = 'Invalid response schema';
|
||||
|
||||
export default function useFetchReport({ hash }: Params) {
|
||||
const query = useApiQuery(RESOURCE_NAME, {
|
||||
pathParams: { hash },
|
||||
queryOptions: {
|
||||
select: (response) => {
|
||||
const parsedResponse = v.safeParse(SolidityScanSchema, response);
|
||||
|
||||
if (!parsedResponse.success) {
|
||||
throw Error(ERROR_NAME);
|
||||
}
|
||||
|
||||
return parsedResponse.output;
|
||||
},
|
||||
enabled: Boolean(hash),
|
||||
placeholderData: SOLIDITY_SCAN_REPORT,
|
||||
retry: 0,
|
||||
},
|
||||
});
|
||||
|
||||
const errorMessage = query.error && 'message' in query.error ? query.error.message : undefined;
|
||||
|
||||
React.useEffect(() => {
|
||||
if (errorMessage === ERROR_NAME) {
|
||||
fetch('/node-api/monitoring/invalid-api-schema', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
resource: RESOURCE_NAME,
|
||||
url: buildUrl(RESOURCE_NAME, { hash }, undefined, true),
|
||||
}),
|
||||
});
|
||||
}
|
||||
}, [ errorMessage, hash ]);
|
||||
|
||||
return query;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { SolidityscanReport } from 'types/api/contract';
|
||||
import type { SolidityScanReport } from 'lib/solidityScan/schema';
|
||||
|
||||
export const solidityscanReportAverage: SolidityscanReport = {
|
||||
export const solidityscanReportAverage: SolidityScanReport = {
|
||||
scan_report: {
|
||||
contractname: 'foo',
|
||||
scan_status: 'scan_done',
|
||||
@@ -13,17 +13,13 @@ export const solidityscanReportAverage: SolidityscanReport = {
|
||||
low: 2,
|
||||
medium: 0,
|
||||
},
|
||||
lines_analyzed_count: 18,
|
||||
scan_time_taken: 1,
|
||||
score: '3.61',
|
||||
score_v2: '72.22',
|
||||
threat_score: '94.74',
|
||||
},
|
||||
scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout',
|
||||
},
|
||||
};
|
||||
|
||||
export const solidityscanReportGreat: SolidityscanReport = {
|
||||
export const solidityscanReportGreat: SolidityScanReport = {
|
||||
scan_report: {
|
||||
contractname: 'foo',
|
||||
scan_status: 'scan_done',
|
||||
@@ -36,17 +32,13 @@ export const solidityscanReportGreat: SolidityscanReport = {
|
||||
low: 0,
|
||||
medium: 0,
|
||||
},
|
||||
lines_analyzed_count: 18,
|
||||
scan_time_taken: 1,
|
||||
score: '3.61',
|
||||
score_v2: '100',
|
||||
threat_score: '94.74',
|
||||
},
|
||||
scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout',
|
||||
},
|
||||
};
|
||||
|
||||
export const solidityscanReportLow: SolidityscanReport = {
|
||||
export const solidityscanReportLow: SolidityScanReport = {
|
||||
scan_report: {
|
||||
contractname: 'foo',
|
||||
scan_status: 'scan_done',
|
||||
@@ -59,11 +51,7 @@ export const solidityscanReportLow: SolidityscanReport = {
|
||||
low: 2,
|
||||
medium: 10,
|
||||
},
|
||||
lines_analyzed_count: 18,
|
||||
scan_time_taken: 1,
|
||||
score: '3.61',
|
||||
score_v2: '22.22',
|
||||
threat_score: '94.74',
|
||||
},
|
||||
scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout',
|
||||
},
|
||||
|
||||
Vendored
+1
@@ -21,6 +21,7 @@ declare module "nextjs-routes" {
|
||||
| StaticRoute<"/api/log">
|
||||
| StaticRoute<"/api/media-type">
|
||||
| StaticRoute<"/api/metrics">
|
||||
| StaticRoute<"/api/monitoring/invalid-api-schema">
|
||||
| StaticRoute<"/api/proxy">
|
||||
| StaticRoute<"/api/sprite">
|
||||
| StaticRoute<"/api-docs">
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"react-scroll": "^1.8.7",
|
||||
"swagger-ui-react": "^5.9.0",
|
||||
"use-font-face-observer": "^1.2.1",
|
||||
"valibot": "0.38.0",
|
||||
"viem": "2.10.9",
|
||||
"wagmi": "2.9.2",
|
||||
"xss": "^1.0.14"
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import * as v from 'valibot';
|
||||
|
||||
import metrics from 'lib/monitoring/metrics';
|
||||
|
||||
const PayloadSchema = v.object({
|
||||
resource: v.string(),
|
||||
url: v.string(),
|
||||
});
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
const payload = JSON.parse(req.body);
|
||||
metrics?.invalidApiSchema.inc(v.parse(PayloadSchema, payload));
|
||||
} catch (error) {
|
||||
|
||||
}
|
||||
res.status(200).json({ status: 'ok' });
|
||||
}
|
||||
+4
-6
@@ -1,6 +1,8 @@
|
||||
import type { SmartContract, SolidityscanReport } from 'types/api/contract';
|
||||
import type { SmartContract } from 'types/api/contract';
|
||||
import type { VerifiedContract, VerifiedContractsCounters } from 'types/api/contracts';
|
||||
|
||||
import type { SolidityScanReport } from 'lib/solidityScan/schema';
|
||||
|
||||
import { ADDRESS_PARAMS } from './addressParams';
|
||||
|
||||
export const CONTRACT_CODE_UNVERIFIED = {
|
||||
@@ -78,7 +80,7 @@ export const VERIFIED_CONTRACTS_COUNTERS: VerifiedContractsCounters = {
|
||||
new_verified_smart_contracts_24h: '1234',
|
||||
};
|
||||
|
||||
export const SOLIDITYSCAN_REPORT: SolidityscanReport = {
|
||||
export const SOLIDITY_SCAN_REPORT: SolidityScanReport = {
|
||||
scan_report: {
|
||||
contractname: 'BullRunners',
|
||||
scan_status: 'scan_done',
|
||||
@@ -91,11 +93,7 @@ export const SOLIDITYSCAN_REPORT: SolidityscanReport = {
|
||||
low: 2,
|
||||
medium: 0,
|
||||
},
|
||||
lines_analyzed_count: 18,
|
||||
scan_time_taken: 1,
|
||||
score: '3.61',
|
||||
score_v2: '72.22',
|
||||
threat_score: '94.74',
|
||||
},
|
||||
scanner_reference_url: 'https://solidityscan.com/quickscan/0xc1EF7811FF2ebFB74F80ed7423f2AdAA37454be2/blockscout/eth-goerli?ref=blockscout',
|
||||
},
|
||||
|
||||
@@ -107,32 +107,6 @@ export interface SmartContractVerificationError {
|
||||
name?: Array<string>;
|
||||
}
|
||||
|
||||
// it's an external API proxy, we can't guarantee the responce types
|
||||
export type SolidityscanReport = {
|
||||
scan_report?: {
|
||||
contractname?: string;
|
||||
scan_status?: string;
|
||||
scan_summary?: {
|
||||
issue_severity_distribution?: SolidityscanReportSeverityDistribution;
|
||||
lines_analyzed_count?: number;
|
||||
scan_time_taken?: number;
|
||||
score?: string;
|
||||
score_v2?: string;
|
||||
threat_score?: string;
|
||||
};
|
||||
scanner_reference_url?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type SolidityscanReportSeverityDistribution = {
|
||||
critical?: number;
|
||||
gas?: number;
|
||||
high?: number;
|
||||
informational?: number;
|
||||
low?: number;
|
||||
medium?: number;
|
||||
};
|
||||
|
||||
type SmartContractSecurityAudit = {
|
||||
audit_company_name: string;
|
||||
audit_publish_date: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { SolidityscanReport, SolidityscanReportSeverityDistribution } from 'types/api/contract';
|
||||
import type { SolidityScanReport, SolidityScanReportSeverityDistribution } from 'lib/solidityScan/schema';
|
||||
|
||||
export type MarketplaceAppPreview = {
|
||||
id: string;
|
||||
@@ -54,12 +54,12 @@ export type MarketplaceAppSecurityReport = {
|
||||
solidityScanContractsNumber: number;
|
||||
securityScore: number;
|
||||
totalIssues?: number;
|
||||
issueSeverityDistribution: SolidityscanReportSeverityDistribution;
|
||||
issueSeverityDistribution: SolidityScanReportSeverityDistribution;
|
||||
};
|
||||
contractsData: Array<{
|
||||
address: string;
|
||||
isVerified: boolean;
|
||||
solidityScanReport?: SolidityscanReport['scan_report'] | null;
|
||||
solidityScanReport?: SolidityScanReport['scan_report'] | null;
|
||||
}>;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const addressHash = 'hash';
|
||||
|
||||
test('average report +@dark-mode +@mobile', async({ render, mockApiResponse, page }) => {
|
||||
await mockApiResponse(
|
||||
'contract_solidityscan_report',
|
||||
'contract_solidity_scan_report',
|
||||
solidityscanReportMock.solidityscanReportAverage,
|
||||
{ pathParams: { hash: addressHash } },
|
||||
);
|
||||
@@ -23,7 +23,7 @@ test('average report +@dark-mode +@mobile', async({ render, mockApiResponse, pag
|
||||
|
||||
test('great report', async({ render, mockApiResponse, page }) => {
|
||||
await mockApiResponse(
|
||||
'contract_solidityscan_report',
|
||||
'contract_solidity_scan_report',
|
||||
solidityscanReportMock.solidityscanReportGreat,
|
||||
{ pathParams: { hash: addressHash } },
|
||||
);
|
||||
@@ -41,7 +41,7 @@ test('great report', async({ render, mockApiResponse, page }) => {
|
||||
|
||||
test('low report', async({ render, mockApiResponse, page }) => {
|
||||
await mockApiResponse(
|
||||
'contract_solidityscan_report',
|
||||
'contract_solidity_scan_report',
|
||||
solidityscanReportMock.solidityscanReportLow,
|
||||
{ pathParams: { hash: addressHash } },
|
||||
);
|
||||
|
||||
@@ -5,8 +5,7 @@ import React from 'react';
|
||||
// Probably because of the gradient
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import solidityScanIcon from 'icons/brands/solidity_scan.svg';
|
||||
import useApiQuery from 'lib/api/useApiQuery';
|
||||
import { SOLIDITYSCAN_REPORT } from 'stubs/contract';
|
||||
import useFetchReport from 'lib/solidityScan/useFetchReport';
|
||||
import Popover from 'ui/shared/chakra/Popover';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton';
|
||||
@@ -20,16 +19,9 @@ interface Props {
|
||||
const SolidityscanReport = ({ hash }: Props) => {
|
||||
const { isOpen, onToggle, onClose } = useDisclosure();
|
||||
|
||||
const { data, isPlaceholderData, isError } = useApiQuery('contract_solidityscan_report', {
|
||||
pathParams: { hash },
|
||||
queryOptions: {
|
||||
enabled: Boolean(hash),
|
||||
placeholderData: SOLIDITYSCAN_REPORT,
|
||||
retry: 0,
|
||||
},
|
||||
});
|
||||
const { data, isPlaceholderData, isError } = useFetchReport({ hash });
|
||||
|
||||
if (isError || !data?.scan_report?.scan_summary) {
|
||||
if (isError || !data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -67,7 +59,7 @@ const SolidityscanReport = ({ hash }: Props) => {
|
||||
<SolidityscanReportDetails vulnerabilities={ vulnerabilities } vulnerabilitiesCount={ vulnerabilitiesCount }/>
|
||||
</Box>
|
||||
) }
|
||||
<LinkExternal href={ data?.scan_report.scanner_reference_url }>View full report</LinkExternal>
|
||||
<LinkExternal href={ data.scan_report.scanner_reference_url }>View full report</LinkExternal>
|
||||
</PopoverBody>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -42,7 +42,7 @@ const ContractListModal = ({ onClose, onBack, type, contracts }: Props) => {
|
||||
return contracts
|
||||
.filter((contract) => Boolean(contract.solidityScanReport))
|
||||
.sort((a, b) =>
|
||||
(parseFloat(b.solidityScanReport?.scan_summary?.score_v2 ?? '0')) - (parseFloat(a.solidityScanReport?.scan_summary?.score_v2 ?? '0')),
|
||||
(parseFloat(b.solidityScanReport?.scan_summary.score_v2 ?? '0')) - (parseFloat(a.solidityScanReport?.scan_summary.score_v2 ?? '0')),
|
||||
);
|
||||
case ContractListTypes.VERIFIED:
|
||||
return contracts.filter((contract) => contract.isVerified);
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
import { Box, Text, PopoverTrigger, PopoverBody, PopoverContent, useDisclosure, Icon } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { SolidityscanReport } from 'types/api/contract';
|
||||
|
||||
import config from 'configs/app';
|
||||
// This icon doesn't work properly when it is in the sprite
|
||||
// Probably because of the gradient
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import solidityScanIcon from 'icons/brands/solidity_scan.svg';
|
||||
import * as mixpanel from 'lib/mixpanel/index';
|
||||
import type { SolidityScanReport } from 'lib/solidityScan/schema';
|
||||
import Popover from 'ui/shared/chakra/Popover';
|
||||
import LinkExternal from 'ui/shared/links/LinkExternal';
|
||||
import SolidityscanReportButton from 'ui/shared/solidityscanReport/SolidityscanReportButton';
|
||||
@@ -16,7 +15,7 @@ import SolidityscanReportDetails from 'ui/shared/solidityscanReport/Solidityscan
|
||||
import SolidityscanReportScore from 'ui/shared/solidityscanReport/SolidityscanReportScore';
|
||||
|
||||
type Props = {
|
||||
securityReport?: SolidityscanReport['scan_report'] | null;
|
||||
securityReport?: SolidityScanReport['scan_report'] | null;
|
||||
}
|
||||
|
||||
const ContractSecurityReport = ({ securityReport }: Props) => {
|
||||
@@ -27,11 +26,11 @@ const ContractSecurityReport = ({ securityReport }: Props) => {
|
||||
onToggle();
|
||||
}, [ onToggle ]);
|
||||
|
||||
if (!securityReport?.scan_summary?.score_v2) {
|
||||
if (!securityReport) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const url = securityReport?.scanner_reference_url;
|
||||
const url = securityReport.scanner_reference_url;
|
||||
const {
|
||||
score_v2: securityScore,
|
||||
issue_severity_distribution: issueSeverityDistribution,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Box, Flex, Text, Grid, useColorModeValue, chakra } from '@chakra-ui/react';
|
||||
import React from 'react';
|
||||
|
||||
import type { SolidityscanReportSeverityDistribution } from 'types/api/contract';
|
||||
import type { SolidityScanReportSeverityDistribution } from 'lib/solidityScan/schema';
|
||||
|
||||
type DistributionItem = {
|
||||
id: keyof SolidityscanReportSeverityDistribution;
|
||||
id: keyof SolidityScanReportSeverityDistribution;
|
||||
name: string;
|
||||
color: string;
|
||||
}
|
||||
@@ -19,13 +19,13 @@ const DISTRIBUTION_ITEMS: Array<DistributionItem> = [
|
||||
];
|
||||
|
||||
interface Props {
|
||||
vulnerabilities: SolidityscanReportSeverityDistribution;
|
||||
vulnerabilities: SolidityScanReportSeverityDistribution;
|
||||
vulnerabilitiesCount: number;
|
||||
}
|
||||
|
||||
type ItemProps = {
|
||||
item: DistributionItem;
|
||||
vulnerabilities: SolidityscanReportSeverityDistribution;
|
||||
vulnerabilities: SolidityScanReportSeverityDistribution;
|
||||
vulnerabilitiesCount: number;
|
||||
}
|
||||
|
||||
|
||||
@@ -15904,6 +15904,11 @@ v8-to-istanbul@^9.0.1:
|
||||
"@types/istanbul-lib-coverage" "^2.0.1"
|
||||
convert-source-map "^1.6.0"
|
||||
|
||||
[email protected]:
|
||||
version "0.38.0"
|
||||
resolved "https://registry.yarnpkg.com/valibot/-/valibot-0.38.0.tgz#2d035d2a5bd36e8ea8b48b56d44552ace1a7616f"
|
||||
integrity sha512-RCJa0fetnzp+h+KN9BdgYOgtsMAG9bfoJ9JSjIhFHobKWVWyzM3jjaeNTdpFK9tQtf3q1sguXeERJ/LcmdFE7w==
|
||||
|
||||
[email protected]:
|
||||
version "1.11.2"
|
||||
resolved "https://registry.yarnpkg.com/valtio/-/valtio-1.11.2.tgz#b8049c02dfe65620635d23ebae9121a741bb6530"
|
||||
|
||||
Reference in New Issue
Block a user