diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx index 2a8f824b..0055dda1 100644 --- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx @@ -25,6 +25,7 @@ import { WorkflowParameterInput } from "../../WorkflowParameterInput"; import { ParametersState } from "../types"; import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { validateBitwardenLoginCredential } from "./util"; +import { HelpTooltip } from "@/components/HelpTooltip"; type Props = { type: WorkflowEditorParameterType; @@ -273,19 +274,32 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) { {type === "credential" && credentialType === "onepassword" && ( <>
- +
+ + +
setVaultId(e.target.value)} />
- +
+ + +
setItemId(e.target.value)} />
+
+
+ * Credit Cards: Due to a 1Password limitation, add the + expiration date as a separate text field named "Expire Date" + in the format MM/YYYY (e.g. 09/2027). +
+
)} {type === "credential" && credentialType === "azurevault" && ( diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx index 73ccc4e4..d520858e 100644 --- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx @@ -31,6 +31,7 @@ import { } from "../types"; import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { validateBitwardenLoginCredential } from "./util"; +import { HelpTooltip } from "@/components/HelpTooltip"; type Props = { type: WorkflowEditorParameterType; @@ -357,19 +358,32 @@ function WorkflowParameterEditPanel({ {type === "credential" && credentialType === "onepassword" && ( <>
- +
+ + +
setVaultId(e.target.value)} />
- +
+ + +
setOpItemId(e.target.value)} />
+
+
+ Credit Cards: Due to a 1Password limitation, add the + expiration date as a separate text field named “Expire Date” + in the format MM/YYYY (e.g. 09/2027). +
+
)} {type === "credential" && credentialType === "azurevault" && ( diff --git a/skyvern/forge/sdk/workflow/context_manager.py b/skyvern/forge/sdk/workflow/context_manager.py index ac3a5197..e2537c09 100644 --- a/skyvern/forge/sdk/workflow/context_manager.py +++ b/skyvern/forge/sdk/workflow/context_manager.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any, Self import structlog from jinja2.sandbox import SandboxedEnvironment +from onepassword import ItemFieldType from onepassword.client import Client as OnePasswordClient from skyvern.config import settings @@ -452,24 +453,61 @@ class WorkflowRunContext: "context": "These values are placeholders. When you type this in, the real value gets inserted (For security reasons)", } - # Process all fields + # Process all fields generically so it covers passwords and credit cards for field in item.fields: - if field.value is None: + if not field.value or field.field_type == ItemFieldType.UNSUPPORTED: continue - random_secret_id = self.generate_random_secret_id() + + # ignore irrelevant fields to avoid confusing AI + if field.id in ["validFrom", "interest", "issuenumber"]: + continue + field_type = field.field_type.value.lower() if field_type == "totp": + random_secret_id = self.generate_random_secret_id() totp_secret_id = f"{random_secret_id}_totp" self.secrets[totp_secret_id] = OnePasswordConstants.TOTP totp_secret_value = self.totp_secret_value_key(totp_secret_id) self.secrets[totp_secret_value] = parse_totp_secret(field.value) self.values[parameter.key]["totp"] = totp_secret_id + elif field.title and field.title.lower() in ["expire date", "expiry date", "expiration date"]: + parts = [part.strip() for part in field.value.strip().split("/")] + + if len(parts) == 2: + month, year_part = parts + month = month.zfill(2) # ensure '5' becomes '05' + + if len(year_part) == 4: + year = year_part[2:] # 2025 -> 25 + else: + year = year_part + + self._add_secret_parameter_value(parameter, "card_exp_month", month) + self._add_secret_parameter_value(parameter, "card_exp_year", year) + if len(year) == 2: + self._add_secret_parameter_value(parameter, "card_exp_mmyy", f"{month}/{year}") + self._add_secret_parameter_value(parameter, "card_exp_mmyyyy", f"{month}/20{year}") + else: + # store the 1password-provided value additionally + self._add_secret_parameter_value(parameter, "card_exp", field.value) + else: + # fallback on the 1password-provided value + self._add_secret_parameter_value(parameter, "card_exp", field.value) else: - # this will be the username or password or other field - key = field.id.replace(" ", "_") - secret_id = f"{random_secret_id}_{key}" - self.secrets[secret_id] = field.value - self.values[parameter.key][key] = secret_id + # using more descriptive keys than 1password provides by default + if field.id == "ccnum": + self._add_secret_parameter_value(parameter, "card_number", field.value) + elif field.id == "cardholder": + self._add_secret_parameter_value(parameter, "card_holder_name", field.value) + elif field.id == "cvv": + self._add_secret_parameter_value(parameter, "card_cvv", field.value) + else: + # this will be the username, password or other fields + self._add_secret_parameter_value(parameter, field.id.replace(" ", "_"), field.value) + + # Secure Note support + if item.notes: + self._add_secret_parameter_value(parameter, "notes", item.notes) async def register_bitwarden_login_credential_parameter_value( self, @@ -981,6 +1019,15 @@ class WorkflowRunContext: azure_vault_client = AsyncAzureVaultClient.create_default() return azure_vault_client + def _add_secret_parameter_value(self, parameter: Parameter, key: str, value: str) -> None: + if parameter.key not in self.values: + raise ValueError(f"{parameter.key} is missing") + + random_secret_id = self.generate_random_secret_id() + secret_id = f"{random_secret_id}_{key}" + self.secrets[secret_id] = value + self.values[parameter.key][key] = secret_id + class WorkflowContextManager: aws_client: AsyncAWSClient