Skip to content
Merged
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
14 changes: 7 additions & 7 deletions modules/bigbio/onsite/main.nf
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
process ONSITE {
tag "${meta.id}"
tag "${meta.mzml_id}"
label 'process_medium'
label 'onsite'

Expand All @@ -20,7 +20,7 @@ process ONSITE {

script:
def args = task.ext.args ?: ''
prefix = task.ext.prefix ?: "${meta.id}"
prefix = task.ext.prefix ?: "${meta.mzml_id}"

// Algorithm selection: lucxor (default), ascore, or phosphors
def algorithm = params.onsite_algorithm ?: 'lucxor'
Expand All @@ -42,7 +42,7 @@ process ONSITE {

if (algorithm == 'ascore') {
// AScore: uses -in, -id, -out, --fragment-mass-unit
fragment_unit = params.onsite_fragment_unit ?: 'Da'
fragment_unit = params.onsite_fragment_error_units ?: 'Da'
def optional_flags = [add_decoys, compute_all_scores, debug].findAll { a -> a }.join(' \\\n ')
algorithm_cmd = """
onsite ascore \\
Expand All @@ -55,7 +55,7 @@ process ONSITE {
}
else if (algorithm == 'phosphors') {
// PhosphoRS: uses -in, -id, -out, --fragment-mass-unit
fragment_unit = params.onsite_fragment_unit ?: 'Da'
fragment_unit = params.onsite_fragment_error_units ?: 'Da'
def optional_flags = [add_decoys, compute_all_scores, debug].findAll { a -> a }.join(' \\\n ')
algorithm_cmd = """
onsite phosphors \\
Expand All @@ -82,10 +82,10 @@ process ONSITE {
def disable_split_by_charge = params.onsite_disable_split_by_charge ? '--disable-split-by-charge' : ''

// Optional target modifications - default for LucXor includes decoy
def target_mods = params.onsite_target_modifications ? "--target-modifications ${params.onsite_target_modifications}" : "--target-modifications 'Phospho(S),Phospho(T),Phospho(Y),PhosphoDecoy(A)'"
def neutral_losses = params.onsite_neutral_losses ? "--neutral-losses ${params.onsite_neutral_losses}" : "--neutral-losses 'sty -H3PO4 -97.97690'"
def target_mods = params.onsite_target_modifications ? "--target-modifications '${params.onsite_target_modifications}'" : "--target-modifications 'Phospho(S),Phospho(T),Phospho(Y),PhosphoDecoy(A)'"
def neutral_losses = params.onsite_neutral_losses ? "--neutral-losses '${params.onsite_neutral_losses}'" : "--neutral-losses 'sty -H3PO4 -97.97690'"
def decoy_mass = params.onsite_decoy_mass ? "--decoy-mass ${params.onsite_decoy_mass}" : "--decoy-mass 79.966331"
def decoy_losses = params.onsite_decoy_neutral_losses ? "--decoy-neutral-losses ${params.onsite_decoy_neutral_losses}" : "--decoy-neutral-losses 'X -H3PO4 -97.97690'"
def decoy_losses = params.onsite_decoy_neutral_losses ? "--decoy-neutral-losses '${params.onsite_decoy_neutral_losses}'" : "--decoy-neutral-losses 'X -H3PO4 -97.97690'"
Comment on lines +85 to +88

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Shell injection vulnerability: unescaped single quotes in user-supplied parameters.

Wrapping user-supplied values in single quotes does not prevent shell injection if the value itself contains a single quote. An attacker could break out of the quoting context.

Example: If params.onsite_target_modifications = "Phospho(S)'; rm -rf /; echo '", the resulting command becomes:

--target-modifications 'Phospho(S)'; rm -rf /; echo ''

Escape embedded single quotes before interpolation:

🛡️ Proposed fix: escape single quotes in user values
         // Optional target modifications - default for LucXor includes decoy
-        def target_mods = params.onsite_target_modifications ? "--target-modifications '${params.onsite_target_modifications}'" : "--target-modifications 'Phospho(S),Phospho(T),Phospho(Y),PhosphoDecoy(A)'"
-        def neutral_losses = params.onsite_neutral_losses ? "--neutral-losses '${params.onsite_neutral_losses}'" : "--neutral-losses 'sty -H3PO4 -97.97690'"
+        def target_mods_val = params.onsite_target_modifications ? params.onsite_target_modifications.toString().replaceAll(/'/, "'\"'\"'") : "Phospho(S),Phospho(T),Phospho(Y),PhosphoDecoy(A)"
+        def target_mods = "--target-modifications '${target_mods_val}'"
+        def neutral_losses_val = params.onsite_neutral_losses ? params.onsite_neutral_losses.toString().replaceAll(/'/, "'\"'\"'") : "sty -H3PO4 -97.97690"
+        def neutral_losses = "--neutral-losses '${neutral_losses_val}'"
         def decoy_mass = params.onsite_decoy_mass ? "--decoy-mass ${params.onsite_decoy_mass}" : "--decoy-mass 79.966331"
-        def decoy_losses = params.onsite_decoy_neutral_losses ? "--decoy-neutral-losses '${params.onsite_decoy_neutral_losses}'" : "--decoy-neutral-losses 'X -H3PO4 -97.97690'"
+        def decoy_losses_val = params.onsite_decoy_neutral_losses ? params.onsite_decoy_neutral_losses.toString().replaceAll(/'/, "'\"'\"'") : "X -H3PO4 -97.97690"
+        def decoy_losses = "--decoy-neutral-losses '${decoy_losses_val}'"

The pattern '\"'\"' ends the single-quoted string, appends a double-quoted single quote, then resumes single quoting—a standard shell idiom for embedding literal single quotes.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modules/bigbio/onsite/main.nf` around lines 85 - 88, The shell command
construction is vulnerable to injection because user-supplied parameters wrapped
in single quotes are not escaped—an attacker could inject a single quote to
break out of the quoting context. Escape embedded single quotes in each
user-supplied parameter before interpolation for the variables target_mods,
neutral_losses, decoy_mass, and decoy_losses. Apply the standard shell idiom
pattern to each `params.onsite_*` reference to convert any embedded single
quotes into a safe format that terminates the quoted string, appends an escaped
quote, and resumes quoting, ensuring that malicious input cannot break the
command structure.


def optional_flags = [disable_split_by_charge, compute_all_scores, debug].findAll { a -> a }.join(' \\\n ')
algorithm_cmd = """
Expand Down
Loading