-
Notifications
You must be signed in to change notification settings - Fork 4
feat(artifacts): verification #82
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
kahboom
wants to merge
24
commits into
main
Choose a base branch
from
artifact-verification
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
57e8f5c
feat(artifacts): add artifacts queries, mocks
kahboom 9628030
feat(artifacts): add route and setup component
kahboom 317bf40
fix(sidebar): consistent casing
kahboom 9d19918
feat(artifacts): verification first pass
kahboom faae8f3
chore(artifacts): split up components
kahboom c23869b
fix: separated ArtifactSignatureItem component
stanislavsemeniuk c827553
:robot: Updated timestamp file to 2025-11-12,12-45-10.
tommyd450 8864666
update client/openapi/console.yaml
kahboom 949712c
feat: changed the way isExpanded works inside SignatureItem, used cer…
stanislavsemeniuk a1b875e
Assisted-by: ChatGPT
kahboom d1d994a
fix: copy pem for certificate
kahboom fb7560d
fix: lint
kahboom 6196eb2
feat: used mock datat types for implementing proper signatureItem com…
stanislavsemeniuk fdf952b
fix: cleaned usage of key attribute as a props
stanislavsemeniuk 0007c80
feat(artifacts): add computed verification status
kahboom 233cb09
chore: move function to utils to enhance testability
kahboom 59b033a
feat(artifacts): digest, identities, and time coherence enhancements
kahboom 9ef8988
extract RekorEntryPanel into a separate reusable component, made atte…
stanislavsemeniuk aaee47a
Merge pull request #87 from securesign/artifact-signatues-and-attesta…
stanislavsemeniuk 9e3515d
chore: move view-model interfaces to own file
kahboom a24fe8e
chore: move view-model interfaces to own file
kahboom 2c6a26d
chore: remove content header, margin customizations
kahboom 75aa487
fix: import of ParsedCertificate
stanislavsemeniuk a1d3471
fix: wrong copying of digest inside signatures and attestations header
stanislavsemeniuk File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| import { Fragment, useRef, useState } from "react"; | ||
| import { | ||
| Button, | ||
| Content, | ||
| Flex, | ||
| FlexItem, | ||
| Form, | ||
| FormGroup, | ||
| FormGroupLabelHelp, | ||
| FormHelperText, | ||
| HelperText, | ||
| HelperTextItem, | ||
| PageSection, | ||
| Popover, | ||
| TextInput, | ||
| } from "@patternfly/react-core"; | ||
| import { useFetchArtifactsImageData, useVerifyArtifact } from "@app/queries/artifacts"; | ||
| import { LoadingWrapper } from "@app/components/LoadingWrapper"; | ||
| import { ArtifactResults } from "./components/ArtifactResults"; | ||
| import { Controller, useForm } from "react-hook-form"; | ||
| import { ExclamationCircleIcon } from "@patternfly/react-icons"; | ||
|
|
||
| const PLACEHOLDER_URI = "docker.io/library/nginx:latest"; | ||
|
|
||
| interface FormInputs { | ||
| searchInput: string; | ||
| } | ||
|
|
||
| export const Artifacts = () => { | ||
| const [artifactUri, setArtifactUri] = useState<string | null>(); | ||
| const labelHelpRef = useRef(null); | ||
|
|
||
| const { | ||
| artifact, | ||
| isFetching: isFetchingArtifactMetadata, | ||
| fetchError: fetchErrorArtifactMetadata, | ||
| } = useFetchArtifactsImageData({ uri: artifactUri }); | ||
|
|
||
| const { mutate: verifyArtifact, data: verification, error: verifyError } = useVerifyArtifact(); | ||
|
|
||
| const { | ||
| control, | ||
| handleSubmit, | ||
| watch, | ||
| formState: { errors }, | ||
| } = useForm<FormInputs>({ | ||
| mode: "all", | ||
| reValidateMode: "onChange", | ||
| defaultValues: { | ||
| searchInput: "", | ||
| }, | ||
| }); | ||
|
|
||
| const onSubmit = (data: FormInputs) => { | ||
| const uri = data.searchInput?.trim(); | ||
| if (!uri) return; | ||
| setArtifactUri(uri); | ||
| // kick off verification for this URI as well | ||
| verifyArtifact({ uri, expectedSAN: null }); | ||
| }; | ||
|
|
||
| const query = watch("searchInput"); | ||
| const isEmpty = query.trim().length === 0; | ||
|
|
||
| return ( | ||
| <Fragment> | ||
| <PageSection variant="default"> | ||
| <Content> | ||
| <h1>Artifacts</h1> | ||
| <p>Search for an artifact.</p> | ||
| </Content> | ||
| </PageSection> | ||
| <PageSection> | ||
| <Form onSubmit={(e) => void handleSubmit(onSubmit)(e)}> | ||
| <Flex> | ||
| <Flex direction={{ default: "column" }} flex={{ default: "flex_3" }}> | ||
| <FlexItem> | ||
| <Controller | ||
| name="searchInput" | ||
| control={control} | ||
| rules={{ required: { value: true, message: "A URI is required" } }} | ||
| render={({ field, fieldState }) => ( | ||
| <FormGroup | ||
| label="Container Image URI" | ||
| labelHelp={ | ||
| <Popover | ||
| triggerRef={labelHelpRef} | ||
| headerContent={<div>Uniform Resource Identifier (URI)</div>} | ||
| bodyContent={ | ||
| <div> | ||
| The URI identifies where a resource, like a container image, lives (e.g.,{" "} | ||
| {PLACEHOLDER_URI}) | ||
| </div> | ||
| } | ||
| > | ||
| <FormGroupLabelHelp ref={labelHelpRef} aria-label="More info for URI field" /> | ||
| </Popover> | ||
| } | ||
| isRequired | ||
| fieldId="uri" | ||
| > | ||
| <TextInput | ||
| aria-label={`uri input field`} | ||
| {...field} | ||
| type="text" | ||
| name="searchInput" | ||
| id="uri" | ||
| aria-describedby="uri-helper" | ||
| aria-invalid={errors.searchInput ? "true" : "false"} | ||
| placeholder={PLACEHOLDER_URI} | ||
| validated={fieldState.invalid ? "error" : "default"} | ||
| /> | ||
| {fieldState.invalid && ( | ||
| <FormHelperText> | ||
| <HelperText> | ||
| <HelperTextItem icon={<ExclamationCircleIcon />} variant={"error"}> | ||
| {fieldState.invalid ? fieldState.error?.message : <span>A value is required</span>} | ||
| </HelperTextItem> | ||
| </HelperText> | ||
| </FormHelperText> | ||
| )} | ||
| </FormGroup> | ||
| )} | ||
| ></Controller> | ||
| </FlexItem> | ||
| </Flex> | ||
| <Flex | ||
| direction={{ default: "column" }} | ||
| alignSelf={{ default: "alignSelfFlexStart" }} | ||
| flex={{ default: "flex_1" }} | ||
| > | ||
| <FlexItem style={{ marginTop: "2em" }}> | ||
| <Button | ||
| variant="primary" | ||
| id="search-form-button" | ||
| isBlock={true} | ||
| isDisabled={isEmpty} | ||
| type="submit" | ||
| spinnerAriaLabel="Loading" | ||
| spinnerAriaLabelledBy="search-form-button" | ||
| > | ||
| Search | ||
| </Button> | ||
| </FlexItem> | ||
| </Flex> | ||
| </Flex> | ||
| </Form> | ||
| </PageSection> | ||
| <PageSection> | ||
| <LoadingWrapper isFetching={isFetchingArtifactMetadata} fetchError={fetchErrorArtifactMetadata ?? verifyError}> | ||
| {artifact && verification && <ArtifactResults artifact={artifact} verification={verification} />} | ||
| </LoadingWrapper> | ||
| </PageSection> | ||
| </Fragment> | ||
| ); | ||
| }; |
78 changes: 78 additions & 0 deletions
78
client/src/app/pages/Artifacts/components/ArtifactResults.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| import { useState } from "react"; | ||
| import type { ImageMetadataResponse } from "@app/client"; | ||
| import type { ArtifactVerificationViewModel } from "@app/queries/artifacts"; | ||
| import { | ||
| Button, | ||
| Card, | ||
| CardBody, | ||
| CardHeader, | ||
| Content, | ||
| ContentVariants, | ||
| Flex, | ||
| FlexItem, | ||
| Label, | ||
| Panel, | ||
| Tab, | ||
| Tabs, | ||
| TabTitleText, | ||
| } from "@patternfly/react-core"; | ||
| import { ArtifactResultsSummary } from "./ArtifactResultsSummary"; | ||
| import { ArtifactResultsSignatures } from "./ArtifactResultsSignatures"; | ||
| import { ArtifactResultsAttestations } from "./ArtifactResultsAttestations"; | ||
| import { verificationStatusToLabelColor } from "@app/utils/utils"; | ||
|
|
||
| export interface IArtifactResultsProps { | ||
| artifact: ImageMetadataResponse; | ||
| verification: ArtifactVerificationViewModel; | ||
| } | ||
|
|
||
| export const ArtifactResults = ({ artifact, verification }: IArtifactResultsProps) => { | ||
| const [activeTabKey, setActiveTabKey] = useState<string | number>(0); | ||
| const { overallStatus } = verification.summary; | ||
| const { label: verificationLabel, color: verificationLabelColor } = verificationStatusToLabelColor(overallStatus); | ||
|
|
||
| const handleTabClick = ( | ||
| _event: React.MouseEvent<unknown> | React.KeyboardEvent | MouseEvent, | ||
| tabIndex: string | number | ||
| ) => { | ||
| setActiveTabKey(tabIndex); | ||
| }; | ||
|
|
||
| return ( | ||
| <div style={{ margin: "2em auto" }}> | ||
| <p>Showing 1 of 1</p> | ||
| <Card style={{ margin: "1.5em auto 2em", overflowY: "hidden" }}> | ||
| <CardHeader> | ||
| <Content component={ContentVariants.h4} style={{ overflow: "hidden", textOverflow: "ellipsis" }}> | ||
| <Flex className="border"> | ||
| <FlexItem> | ||
| Artifact: <Button variant="plain">{artifact.image}</Button> | ||
| </FlexItem> | ||
| <FlexItem align={{ default: "alignRight" }}> | ||
| <Label color={verificationLabelColor}>{verificationLabel}</Label> | ||
| </FlexItem> | ||
| </Flex> | ||
| </Content> | ||
| </CardHeader> | ||
| <CardBody> | ||
| <Panel style={{ margin: "0.75em auto" }}> | ||
| {/** ARTIFACT RESULTS SUMMARY */} | ||
| <ArtifactResultsSummary artifact={artifact} verification={verification} /> | ||
| </Panel> | ||
| <Panel style={{ marginTop: "1.25em" }}> | ||
| <Tabs activeKey={activeTabKey} onSelect={handleTabClick} aria-label="Artifact results" role="region"> | ||
| <Tab eventKey={0} title={<TabTitleText>Signatures</TabTitleText>} aria-label="Signatures"> | ||
| {/** SIGNATURES */} | ||
| <ArtifactResultsSignatures signatures={verification.signatures} /> | ||
| </Tab> | ||
| <Tab eventKey={1} title={<TabTitleText>Attestations</TabTitleText>} aria-label="Attestations"> | ||
| {/** ATTESTATIONS */} | ||
| <ArtifactResultsAttestations attestations={verification.attestations} /> | ||
| </Tab> | ||
| </Tabs> | ||
| </Panel> | ||
| </CardBody> | ||
| </Card> | ||
| </div> | ||
| ); | ||
| }; | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In multiple lines we are using
styleandContent H4. I would advocate for using the defaults as much as possible. So leave the default sizes of all components and only change the defaults by strict request of UX. It will help us maintain visual consistency with all pages in the long termThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure thing!