Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const DroppableFileInputField: React.FC<DroppableFileInputFieldProps> = ({
onChange && onChange(fileData);
}}
inputFileData={field.value}
inputFieldHelpText={helpText}
filenamePlaceholder={helpText}
aria-describedby={helpText ? `${fieldId}-helper` : undefined}
/>
</FormGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface FieldProps {

export interface DroppableFileInputFieldProps extends FieldProps {
onChange?: (fileData: string) => void;
helpText?: string;
label?: string;
}
export interface BaseInputFieldProps extends FieldProps {
type?: TextInputTypes;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,62 @@ describe('Create key/value secrets', () => {
});
});
});

it('Validate editing text field does not corrupt binary data (OCPBUGS-70273)', () => {
const mixedSecretName = `key-value-mixed-secret-${testName}`;
const textKey = 'textfield';
const textValue = 'original-password';
const updatedTextValue = 'updated-password';
const binaryKey = 'binaryfield';

// Create a secret with both text and binary data using CLI
cy.exec(
`oc create secret generic ${mixedSecretName} -n ${testName} --from-literal=${textKey}=${textValue} --from-file=${binaryKey}=${Cypress.config(
'fileServerFolder',
)}/fixtures/${binaryFilename}`,
);

// Capture the original binary data
cy.exec(
`oc get secret -n ${testName} ${mixedSecretName} --template '{{.data.${binaryKey}}}'`,
).then((originalBinary) => {
// Edit the secret via the console
cy.visit(`/k8s/ns/${testName}/secrets/${mixedSecretName}`);
detailsPage.isLoaded();
detailsPage.clickPageActionFromDropdown('Edit Secret');

// Modify only the text field
cy.byTestID('secret-key')
.should('have.length', 2)
.each(($el) => {
if ($el.val() === textKey) {
// Find the corresponding value textarea and update it
cy.byLegacyTestID('file-input-textarea').first().clear().type(updatedTextValue);
}
});

// Verify binary field shows the binary alert (indicates it's still treated as binary)
cy.byTestID('file-input-binary-alert').should('exist');

secrets.save();
cy.byTestID('loading-indicator').should('not.exist');
detailsPage.isLoaded();

// Verify the text field was updated
secrets.clickRevealValues();
cy.byTestID('copy-to-clipboard').should('contain.text', updatedTextValue);

// Verify the binary data was NOT corrupted
cy.exec(
`oc get secret -n ${testName} ${mixedSecretName} --template '{{.data.${binaryKey}}}'`,
).then((updatedBinary) => {
expect(updatedBinary.stdout).to.equal(originalBinary.stdout);
});

// Cleanup
cy.exec(`oc delete secret -n ${testName} ${mixedSecretName}`, {
failOnNonZeroExit: false,
});
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ export const OpaqueSecretFormEntry: FCC<OpaqueSecretFormEntryProps> = ({
</FormGroup>
<DroppableFileInput
onChange={handleValueChange}
inputFileData={Base64.decode(entry.value)}
inputFileData={entry.isBinary_ ? entry.value : Base64.decode(entry.value)}
isBase64Input={entry.isBinary_}
id={`${key}-value`}
label={t('public~Value')}
inputFieldHelpText={t(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as _ from 'lodash-es';
import { FCC, useState, FormEvent } from 'react';
import * as _ from 'lodash';
import { FCC, useState, useMemo, FormEvent } from 'react';
import { DocumentTitle } from '@console/shared/src/components/document-title/DocumentTitle';
import { useTranslation } from 'react-i18next';
import { Base64 } from 'js-base64';
Expand Down Expand Up @@ -66,6 +66,17 @@ export const SecretFormWrapper: FCC<BaseEditSecretProps_> = (props) => {
return acc;
}, {}),
);
// Store binary data separately to preserve it during edits
const binaryData = useMemo(
() =>
Object.entries(props.obj?.data ?? {}).reduce<Record<string, string>>((acc, [key, value]) => {
if (isBinary(null, Buffer.from(value, 'base64'))) {
acc[key] = value;
}
return acc;
}, {}),
[props.obj?.data],
);
const [base64StringData, setBase64StringData] = useState(props?.obj?.data ?? {});
const [disableForm, setDisableForm] = useState(false);
const title = useSecretTitle(isCreate, formType);
Expand All @@ -74,7 +85,20 @@ export const SecretFormWrapper: FCC<BaseEditSecretProps_> = (props) => {

const onDataChanged = (secretsData) => {
setStringData({ ...secretsData?.stringData });
setBase64StringData({ ...secretsData?.base64StringData });
// Preserve binary values by merging them with form data
// Only backfill missing keys from binaryData, don't overwrite edited entries
const mergedData = Object.entries(binaryData).reduce(
(acc, [key, value]) => {
// Only add binary entry if it's missing from form data
if (acc[key] === undefined) {
acc[key] = value;
}
// Otherwise keep the existing value from form data
return acc;
},
{ ...secretsData?.base64StringData },
);
setBase64StringData(mergedData);
};

const onError = (err) => {
Expand Down
Loading