-
Notifications
You must be signed in to change notification settings - Fork 14
feat: security yaml generation #716
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
Merged
+5,266
−124
Merged
Changes from 23 commits
Commits
Show all changes
28 commits
Select commit
Hold shift + click to select a range
adfbafd
feat: add generate modal yaml and yaml display
gaspergrom 6e8571c
feat: merge branch 'main' into feat/yaml-generation
gaspergrom f180663
feat: add steps usage and flow generation
gaspergrom f5e9d19
feat: steps flow and validations
gaspergrom 77b8838
fix: lint
gaspergrom 2cc7809
Merge branch 'main' into feat/yaml-generation
gaspergrom d1d41a6
feat: steps template
gaspergrom 7d1659f
feat: basic flow
gaspergrom 8af315c
feat: forms
gaspergrom fd469ee
feat: finalize basic yaml generation
gaspergrom 05bf860
fix: lint errors
gaspergrom c1a0900
Merge branch 'main' into feat/yaml-generation
gaspergrom b6a0661
feat: child repository yaml generation
gaspergrom 14a9176
Merge branch 'main' into feat/yaml-generation
gaspergrom e8d1043
feat: comprehensive project settings
gaspergrom f75ebcc
Merge branch 'main' into feat/yaml-generation
gaspergrom c302f7e
fix: main comment
gaspergrom 30d414c
feat: comprehensive project settings
gaspergrom fce992a
fix: lint issues
gaspergrom dea0672
feat: enable steps in comprehensive
gaspergrom 91705d0
Merge branch 'main' into feat/yaml-generation
gaspergrom ded3101
Merge branch 'main' into feat/yaml-generation
gaspergrom 30a9a63
feat: add flow optional display and design fixes
gaspergrom 1528604
feat: fix pnpm lock
gaspergrom 560bed6
fix: pr fixes
gaspergrom d3d9118
fix: responsiveness
gaspergrom e220756
fix: pr comment fixes
gaspergrom 499c1c1
feat: add datepicker and modal body scroll lock
gaspergrom 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
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 |
|---|---|---|
|
|
@@ -36,4 +36,8 @@ robots.txt | |
|
|
||
| # SSL | ||
| certs/ | ||
| certs/* | ||
| certs/* | ||
|
|
||
| # Claude | ||
| CLAUDE.md | ||
| .claude | ||
236 changes: 236 additions & 0 deletions
236
frontend/app/components/modules/project/components/security/yaml/generate-yaml-modal.vue
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,236 @@ | ||
| <!-- | ||
| Copyright (c) 2025 The Linux Foundation and each contributor. | ||
| SPDX-License-Identifier: MIT | ||
| --> | ||
| <template> | ||
| <lfx-modal | ||
| v-model="isModalOpen" | ||
| width="1200px" | ||
| height="900px" | ||
| class="!justify-center" | ||
| content-class="!h-full" | ||
| > | ||
| <div class="flex h-full"> | ||
| <lf-security-generate-yaml-sidebar /> | ||
|
|
||
| <!-- Main content area --> | ||
| <div class="w-2/3 flex flex-col"> | ||
| <!-- Header --> | ||
| <div class="border-b border-neutral-200 px-6 pt-4 pb-5"> | ||
| <div class="flex justify-between items-center"> | ||
| <h1 class="text-2xl font-secondary font-bold text-neutral-900 leading-8"> | ||
| Generate YAML security file | ||
| </h1> | ||
| <lfx-icon-button | ||
| icon="close" | ||
| @click="isModalOpen = false" | ||
| /> | ||
| </div> | ||
| <div | ||
| v-if="type && step >= 0" | ||
| class="flex flex-col gap-2 mt-3" | ||
| > | ||
| <div class="flex items-center gap-3"> | ||
| <lfx-tooltip | ||
| placement="top" | ||
| :content="config?.description || ''" | ||
| :disabled="!config?.description" | ||
| class="flex items-center" | ||
| > | ||
| <lfx-tag | ||
| size="small" | ||
| class="bg-neutral-200 text-neutral-600" | ||
| > | ||
| {{ config?.label }} | ||
| </lfx-tag> | ||
| </lfx-tooltip> | ||
| <p class="text-xs text-neutral-600 !leading-6"> | ||
| <span v-if="type && step >= 0">Step {{ step + 1 }}/{{ steps.length + 1 }} - </span> | ||
| <span v-if="!type || step < 0">Choose YAML file template</span> | ||
| <span v-else-if="step < steps.length">{{ currentStep?.label }}</span> | ||
| <span v-else>Preview & Download</span> | ||
| </p> | ||
| </div> | ||
| <div class="relative"> | ||
| <div class="bg-brand-50 h-1.5 rounded-full w-full" /> | ||
| <div | ||
| class="h-1.5 rounded-full absolute top-0 left-0 transition-all" | ||
| :style="{ width: `${(type ? (step + 1) / (steps.length + 1) : 0) * 100}%` }" | ||
| :class="step < steps.length ? 'bg-brand-500' : 'bg-positive-500'" | ||
| /> | ||
| </div> | ||
| </div> | ||
| </div> | ||
|
|
||
| <!-- Form content --> | ||
| <div class="flex-1 p-6 overflow-auto"> | ||
| <lfx-security-generate-yaml-type | ||
| v-if="!type || step < 0" | ||
| v-model="type" | ||
| /> | ||
| <template v-else> | ||
| <lf-security-generate-yaml-preview | ||
| v-if="step >= steps.length" | ||
| :data="form" | ||
| /> | ||
| <component | ||
| :is="steps[step]?.component" | ||
| v-else-if="!!steps[step]" | ||
| v-model="form" | ||
| /> | ||
| </template> | ||
| </div> | ||
|
|
||
| <!-- Footer --> | ||
| <div class="border-t border-neutral-200 px-6 py-4"> | ||
| <div class="flex items-center justify-between"> | ||
| <lfx-button | ||
| v-if="step >= 0 && type" | ||
| type="tertiary" | ||
| button-style="pill" | ||
| @click="step -= 1" | ||
| > | ||
| <lfx-icon name="angle-left" /> | ||
| Previous | ||
| </lfx-button> | ||
| <div class="flex-grow" /> | ||
| <div class="flex items-center gap-3"> | ||
| <template v-if="step < steps.length"> | ||
| <lfx-button | ||
| button-style="pill" | ||
| type="primary" | ||
| :disabled="!type || $v.$invalid" | ||
| @click="step += 1" | ||
| > | ||
| Next | ||
| <lfx-icon name="angle-right" /> | ||
| </lfx-button> | ||
| </template> | ||
| <template v-else-if="type"> | ||
| <lfx-button | ||
| type="tertiary" | ||
| button-style="pill" | ||
| @click="copyToClipboard" | ||
| > | ||
| <lfx-icon name="clone" /> | ||
| Copy to clipboard | ||
| </lfx-button> | ||
| <lfx-button | ||
| button-style="pill" | ||
| @click="downloadYamlFile" | ||
| > | ||
| <lfx-icon name="arrow-down-to-bracket" /> | ||
| Download YAML file | ||
| </lfx-button> | ||
| </template> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| </lfx-modal> | ||
| </template> | ||
|
|
||
| <script setup lang="ts"> | ||
| import { computed, onMounted, watch } from 'vue' | ||
| import useVuelidate from '@vuelidate/core' | ||
| import LfxModal from '~/components/uikit/modal/modal.vue' | ||
| import LfxButton from '~/components/uikit/button/button.vue' | ||
| import LfxIconButton from '~/components/uikit/icon-button/icon-button.vue' | ||
| import LfSecurityGenerateYamlSidebar from | ||
| '~/components/modules/project/components/security/yaml/generate-yaml-sidebar.vue' | ||
| import LfxIcon from '~/components/uikit/icon/icon.vue' | ||
| import LfSecurityGenerateYamlPreview from | ||
| '~/components/modules/project/components/security/yaml/generate-yaml-preview.vue' | ||
| import LfxSecurityGenerateYamlType from '~/components/modules/project/components/security/yaml/generate-yaml-type.vue' | ||
| import { | ||
| type YamlGenerationConfig, | ||
| yamlGenerationConfig, | ||
| type YamlGenerationStep, | ||
| } from '~/components/modules/project/config/yaml-generation/yaml-generation.config' | ||
| import { getYaml } from '~/components/modules/project/services/js-yaml' | ||
| import LfxTag from '~/components/uikit/tag/tag.vue' | ||
| import LfxTooltip from '~/components/uikit/tooltip/tooltip.vue' | ||
| import useToastService from '~/components/uikit/toast/toast.service' | ||
| import { ToastTypesEnum } from '~/components/uikit/toast/types/toast.types' | ||
|
|
||
| const props = withDefaults( | ||
| defineProps<{ | ||
| modelValue: boolean | ||
| }>(), | ||
| { | ||
| modelValue: false, | ||
| }, | ||
| ) | ||
|
|
||
| const emit = defineEmits<{ | ||
| 'update:modelValue': [value: boolean] | ||
| }>() | ||
|
|
||
| const isModalOpen = computed({ | ||
| get: () => props.modelValue, | ||
| set: (value: boolean) => emit('update:modelValue', value), | ||
| }) | ||
|
|
||
| const { showToast } = useToastService() | ||
|
|
||
| const type = ref('') | ||
| const step = ref(-1) | ||
| const form = ref({}) | ||
|
|
||
| const copyToClipboard = async () => { | ||
| if (navigator.clipboard) { | ||
| const yamlContent = getYaml(form.value) | ||
| await navigator.clipboard.writeText(yamlContent) | ||
| showToast('YAML file content copied to clipboard', ToastTypesEnum.positive, 'circle-check') | ||
| } | ||
| } | ||
|
|
||
| const downloadYamlFile = () => { | ||
| try { | ||
| const yamlContent = getYaml(form.value) | ||
| // eslint-disable-next-line no-undef | ||
| const blob = new Blob([yamlContent], { type: 'application/x-yaml' }) | ||
| const url = URL.createObjectURL(blob) | ||
| const link = document.createElement('a') | ||
| link.href = url | ||
| link.download = 'security.yaml' | ||
| document.body.appendChild(link) | ||
| link.click() | ||
| document.body.removeChild(link) | ||
| URL.revokeObjectURL(url) | ||
| showToast('YAML file successfully downloaded', ToastTypesEnum.positive, 'circle-check') | ||
| } catch (error) { | ||
| console.error('Failed to download YAML file:', error) | ||
gaspergrom marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
| } | ||
|
|
||
| const $v = useVuelidate({}, form) | ||
|
|
||
| const config = computed<YamlGenerationConfig | null>(() => { | ||
| return yamlGenerationConfig[type.value] || null | ||
| }) | ||
|
|
||
| const steps = computed<YamlGenerationStep[]>(() => { | ||
| if (!config.value) return [] | ||
| return config.value.steps || [] | ||
| }) | ||
|
|
||
| const currentStep = computed<YamlGenerationStep | null>(() => { | ||
| return steps.value[step.value] || null | ||
| }) | ||
|
|
||
| watch(type, (newType: string) => { | ||
| form.value = { ...(yamlGenerationConfig[newType]?.template || {}) } | ||
| }) | ||
|
|
||
| onMounted(() => { | ||
| form.value = {} | ||
| }) | ||
| </script> | ||
|
|
||
| <script lang="ts"> | ||
| export default { | ||
| name: 'LfSecurityGenerateYamlModal', | ||
| } | ||
| </script> | ||
48 changes: 48 additions & 0 deletions
48
frontend/app/components/modules/project/components/security/yaml/generate-yaml-preview.vue
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,48 @@ | ||
| <!-- | ||
| Copyright (c) 2025 The Linux Foundation and each contributor. | ||
| SPDX-License-Identifier: MIT | ||
| --> | ||
| <template> | ||
| <div class="h-full flex flex-col"> | ||
| <h2 class="text-lg font-semibold mb-1 leading-7">YAML file preview</h2> | ||
| <p class="text-body-2 leading-4 text-neutral-500"> | ||
| Review your generated YAML file before downloading. You can copy the content or download it as | ||
| a file. | ||
| </p> | ||
|
|
||
| <div class="mt-6 flex items-center gap-2 rounded-lg border border-brand-200 bg-brand-50 p-3"> | ||
| <lfx-icon | ||
| name="circle-info" | ||
| type="solid" | ||
| class="shrink-0 text-brand-600" | ||
| /> | ||
| <p class="text-xs leading-4 text-brand-800"> | ||
| Add the YAML file as <span class="font-semibold font-mono">security.yaml</span> in your | ||
| repository root and commit to enable security assessments. | ||
| </p> | ||
| </div> | ||
|
|
||
| <pre | ||
| lang="yaml" | ||
| class="mt-6 p-4 bg-white border border-neutral-200 rounded-xl overflow-auto text-sm font-mono flex-grow" | ||
| >{{ yaml }}</pre> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script lang="ts" setup> | ||
| import { computed } from 'vue' | ||
| import { getYaml } from '~/components/modules/project/services/js-yaml' | ||
| import LfxIcon from '~/components/uikit/icon/icon.vue' | ||
|
|
||
| const props = defineProps<{ | ||
| data: object | ||
| }>() | ||
|
|
||
| const yaml = computed(() => getYaml(props.data)) | ||
| </script> | ||
|
|
||
| <script lang="ts"> | ||
| export default { | ||
| name: 'LfSecurityGenerateYamlPreview', | ||
| } | ||
| </script> |
95 changes: 95 additions & 0 deletions
95
frontend/app/components/modules/project/components/security/yaml/generate-yaml-sidebar.vue
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,95 @@ | ||
| <!-- | ||
| Copyright (c) 2025 The Linux Foundation and each contributor. | ||
| SPDX-License-Identifier: MIT | ||
| --> | ||
| <template> | ||
| <div class="flex flex-col bg-neutral-50 w-1/3 h-full relative border-r border-neutral-200"> | ||
| <div class="flex flex-col gap-6 p-6"> | ||
| <!-- Why generate a YAML file section --> | ||
| <div> | ||
| <h2 class="text-lg font-semibold text-neutral-900 leading-7 mb-6"> | ||
| Why generate a YAML file? | ||
| </h2> | ||
|
|
||
| <!-- Purpose --> | ||
| <div class="mb-6"> | ||
| <h3 class="text-sm font-semibold text-neutral-900 leading-5 mb-2">Purpose</h3> | ||
| <p class="text-sm text-neutral-600 leading-5"> | ||
| YAML security file provides a standardised way to document, assess, and share a | ||
| project's security practices, ensuring consistency and alignment with the specification | ||
| across repositories. | ||
| <a | ||
| href="https://github.com/ossf/security-insights" | ||
| rel="noopener noreferrer" | ||
| target="_blank" | ||
| class="text-brand-500" | ||
| > | ||
| Learn more | ||
| </a> | ||
| </p> | ||
| </div> | ||
|
|
||
| <!-- Requirements --> | ||
| <div class="mb-6"> | ||
| <div class="flex items-center gap-2 mb-2"> | ||
| <h3 class="text-sm font-semibold text-neutral-900 leading-5">Requirements</h3> | ||
| <lfx-tag | ||
| variation="warning" | ||
| size="small" | ||
| type="solid" | ||
| > | ||
| Admin access | ||
| </lfx-tag> | ||
| </div> | ||
| <p class="text-sm text-neutral-600 leading-5"> | ||
| Repository owner or admin. You'll need write permissions to upload the generated file to | ||
| your GitHub repository. | ||
| </p> | ||
| </div> | ||
|
|
||
| <!-- Information collected --> | ||
| <div> | ||
| <h3 class="text-sm font-semibold text-neutral-900 leading-5 mb-2"> | ||
| Information collected | ||
| </h3> | ||
| <ul class="text-sm text-neutral-600 leading-5 list-disc ml-5"> | ||
| <li>Project details</li> | ||
| <li>Repository details</li> | ||
| <li>Administrator contacts</li> | ||
| <li>Core team members</li> | ||
| <li>License information</li> | ||
| <li>Vulnerability reporting</li> | ||
| <li>Security self-assessment</li> | ||
| </ul> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| <div class="flex-grow" /> | ||
|
|
||
| <!-- Bottom info box --> | ||
| <div class="flex flex-col gap-2 items-start p-6"> | ||
| <lfx-icon | ||
| name="info-circle" | ||
| type="light" | ||
| class="text-neutral-500" | ||
| :size="16" | ||
| /> | ||
| <p class="text-body-1 text-neutral-600"> | ||
| YAML security file specifications are optional, and we don't run strict | ||
| validation on them, but your project will still benefit from documenting | ||
| as many as possible. | ||
| </p> | ||
| </div> | ||
| </div> | ||
| </template> | ||
|
|
||
| <script lang="ts" setup> | ||
| import LfxTag from '~/components/uikit/tag/tag.vue' | ||
| import LfxIcon from '~/components/uikit/icon/icon.vue' | ||
| </script> | ||
|
|
||
| <script lang="ts"> | ||
| export default { | ||
| name: 'LfSecurityGenerateYamlSidebar', | ||
| } | ||
| </script> |
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.