Skip to content
This repository was archived by the owner on Dec 24, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ Grafana supports a wide range of data sources, including Prometheus, MySQL, and
2. Build plugin in development mode and run in watch mode

```bash
# Spins up a Grafana instance first that we can run our plugin on
npm run server

npm run dev
```

Expand Down
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ services:
context: ./.config
args:
grafana_image: ${GRAFANA_IMAGE:-grafana-enterprise}
grafana_version: ${GRAFANA_VERSION:-10.0.3}
grafana_version: ${GRAFANA_VERSION:-10.3.1}
ports:
- 3000:3000/tcp
volumes:
Expand Down
66 changes: 45 additions & 21 deletions src/components/QueryEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,51 @@
import defaults from 'lodash/defaults';
import React, { ChangeEvent, useState } from 'react';
import { Button, Field, InlineField, Input, Select } from '@grafana/ui';
import { QueryEditorProps, SelectableValue } from '@grafana/data';
import { NullifySastSummaryDataSource } from '../datasource';
import { NullifyDataSourceOptions, NullifySastSummaryQueryOptions } from '../types';

import React, { ChangeEvent, PureComponent } from 'react';
import { HorizontalGroup } from '@grafana/ui';
import { QueryEditorProps } from '@grafana/data';
import { NullifySastDataSource } from '../datasource';
import { defaultQuery, NullifyDataSourceOptions, MyQuery } from '../types';
type Props = QueryEditorProps<NullifySastSummaryDataSource, NullifySastSummaryQueryOptions, NullifyDataSourceOptions>;

type Props = QueryEditorProps<NullifySastDataSource, MyQuery, NullifyDataSourceOptions>;

export class QueryEditor extends PureComponent<Props> {
onQueryTextChange = (event: ChangeEvent<HTMLInputElement>) => {
const { onChange, query } = this.props;
onChange({ ...query, queryText: event.target.value });
export function QueryEditor({ query, onChange, onRunQuery }: Props) {
const onRepoIdChange = (event: ChangeEvent<HTMLInputElement>) => {
onChange({ ...query, githubRepositoryId: event.target.value });
};
const onSeverityChange = (new_severity: string) => {
onChange({ ...query, severity: new_severity });
onRunQuery();
};

render() {
const query = defaults(this.props.query, defaultQuery);
const { queryText } = query;
const severity_options: Array<SelectableValue<string>> = [
{ value: '', label: 'ALL' },
{ value: 'LOW', label: 'LOW' },
{ value: 'MEDIUM', label: 'MEDIUM' },
{ value: 'HIGH', label: 'HIGH' },
{ value: 'CRITICAL', label: 'CRITICAL' },
];

return (
<HorizontalGroup>
Queries are not currently supported.
</HorizontalGroup>
);
}
return (
<div style={{ paddingTop: '20px' }}>
<Field
label="Repository ID Filter"
description="Query to filter for only the vulnerabilities from the specified GitHub repository ID. Leave blank to query for all repositories."
>
<Input
onChange={onRepoIdChange}
placeholder="1234"
onBlur={onRunQuery}
value={query.githubRepositoryId || ''}
/>
</Field>
<Field
label="Severity Filter"
description="Query to filter for only the vulnerabilities with the selected severity"
>
<Select
options={severity_options}
value={query.severity ?? ''}
onChange={(v) => onSeverityChange(v.value ?? '')}
/>
</Field>
</div>
);
}
39 changes: 27 additions & 12 deletions src/datasource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,12 @@ import defaults from 'lodash/defaults';
import { getBackendSrv, isFetchError } from '@grafana/runtime';
import { lastValueFrom } from 'rxjs';

import { defaultQuery, MyQuery, NullifyDataSourceOptions, NullifySastSummaryApiResponse } from './types';

import {
NullifySastSummaryDefaultQuery,
NullifySastSummaryQueryOptions,
NullifyDataSourceOptions,
NullifySastSummaryApiResponse,
} from './types';

const prepend_severity_idx = (severity: string) => {
severity = severity.toUpperCase();
Expand All @@ -31,7 +35,10 @@ const prepend_severity_idx = (severity: string) => {
}
};

export class NullifySastDataSource extends DataSourceApi<MyQuery, NullifyDataSourceOptions> {
export class NullifySastSummaryDataSource extends DataSourceApi<
NullifySastSummaryQueryOptions,
NullifyDataSourceOptions
> {
instanceUrl?: string;
apiHostUrl: string;
githubOwnerId: number;
Expand All @@ -43,10 +50,10 @@ export class NullifySastDataSource extends DataSourceApi<MyQuery, NullifyDataSou
this.apiHostUrl = instanceSettings.jsonData.apiHostUrl;
}

async query(options: DataQueryRequest<MyQuery>): Promise<DataQueryResponse> {
async query(options: DataQueryRequest<NullifySastSummaryQueryOptions>): Promise<DataQueryResponse> {
const promises = options.targets.map(async (target) => {
const query = defaults(target, defaultQuery);
const response = await this.request();
const query = defaults(target, NullifySastSummaryDefaultQuery);
const response = await this.request_sast_summary(query);

const datapoints: NullifySastSummaryApiResponse = response.data as unknown as NullifySastSummaryApiResponse;
if (datapoints === undefined || !('vulnerabilities' in datapoints)) {
Expand Down Expand Up @@ -90,16 +97,24 @@ export class NullifySastDataSource extends DataSourceApi<MyQuery, NullifyDataSou
return Promise.all(promises).then((data) => ({ data }));
}

// TODO(jqphu): only one path is supported now, sca/events
async request() {
const response = getBackendSrv().fetch<NullifySastSummaryApiResponse>({
url: `${this.instanceUrl}/grafana_proxy/sast/summary?githubOwnerId=${this.githubOwnerId}`,
async _request<T>(endpoint_path: string, params: Record<string, any> = {}) {
const response = getBackendSrv().fetch<T>({
url: `${this.instanceUrl}/grafana_proxy/${endpoint_path}`,
params: params,
});

return await lastValueFrom(response);
}

filterQuery(query: MyQuery): boolean {
async request_sast_summary(query: NullifySastSummaryQueryOptions) {
return await this._request<NullifySastSummaryApiResponse>('sast/summary', {
githubOwnerId: this.githubOwnerId,
...(query.githubRepositoryId ? { githubRepositoryId: query.githubRepositoryId } : {}),
...(query.severity ? { severity: query.severity } : {}),
});
}

filterQuery(query: NullifySastSummaryQueryOptions): boolean {
if (query.hide) {
return false;
}
Expand All @@ -114,7 +129,7 @@ export class NullifySastDataSource extends DataSourceApi<MyQuery, NullifyDataSou
console.log('Starting test');

try {
const response = await this.request();
const response = await this.request_sast_summary({ refId: 'testDatasource' });
if (response.status === 200) {
return {
status: 'success',
Expand Down
10 changes: 7 additions & 3 deletions src/module.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { DataSourcePlugin } from '@grafana/data';
import { NullifySastDataSource } from './datasource';
import { NullifySastSummaryDataSource } from './datasource';
import { ConfigEditor } from './components/ConfigEditor';
import { QueryEditor } from './components/QueryEditor';
import { MyQuery, NullifyDataSourceOptions } from './types';
import { NullifySastSummaryQueryOptions, NullifyDataSourceOptions } from './types';

export const plugin = new DataSourcePlugin<NullifySastDataSource, MyQuery, NullifyDataSourceOptions>(NullifySastDataSource)
export const plugin = new DataSourcePlugin<
NullifySastSummaryDataSource,
NullifySastSummaryQueryOptions,
NullifyDataSourceOptions
>(NullifySastSummaryDataSource)
.setConfigEditor(ConfigEditor)
.setQueryEditor(QueryEditor);
9 changes: 4 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import { DataSourceJsonData } from '@grafana/data';
import { DataQuery } from '@grafana/schema';

export interface MyQuery extends DataQuery {
queryText?: string;
constant: number;
export interface NullifySastSummaryQueryOptions extends DataQuery {
githubRepositoryId?: string;
severity?: string;
}

export const defaultQuery: Partial<MyQuery> = {
constant: 6.5,
export const NullifySastSummaryDefaultQuery: Partial<NullifySastSummaryQueryOptions> = {
};

export interface Vulnerability {
Expand Down