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
8 changes: 5 additions & 3 deletions assets/js-templates/spa-components.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@
<th scope="row" class="check-column">
<input type="checkbox" name="post[]" v-model="checkedItems" :value="entry.id">
</th>
<td v-for="(header, index) in columns"><span v-html="entry.fields[index]"></span></td>
<td v-for="(header, index) in columns"><span>{{ entry.fields[index] }}</span></td>
<th class="col-entry-details">
<template v-if="status == 'trash'">
<a href="#" @click.prevent="restore(entry.id)"><?php esc_html_e( 'Restore', 'weforms' ); ?></a>
Expand All @@ -96,7 +96,7 @@
<th scope="row" class="check-column">
<input type="checkbox" name="post[]" v-model="checkedItems" :value="entry.id">
</th>
<td v-for="(header, index) in columns"><span v-html="entry.fields[index]"></span></td>
<td v-for="(header, index) in columns"><span>{{ entry.fields[index] }}</span></td>
<th class="col-entry-details">
<template v-if="status == 'trash'">
<a href="#" @click.prevent="restore(entry.id)"><?php esc_html_e( 'Restore', 'weforms' ); ?></a>
Expand Down Expand Up @@ -425,7 +425,9 @@
</div>
<div v-else-if="field.type === 'country_list_field'">{{ getCountryName( field.value ) }}</div>
<div v-else-if="field.type === 'address_field'" v-html="getAddressFieldValue( field.value)"></div>
<div v-else v-html="field.value"></div>
<div v-else-if="field.type === 'textarea_field'" v-html="field.value"></div>
<div v-else-if="field.type === 'image_upload' || field.type === 'file_upload' || field.type === 'signature_field' || field.type === 'checkbox_grid' || field.type === 'multiple_choice_grid'" v-html="field.value"></div>
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

This template now escapes the default field renderer ({{ field.value }}), which means field types that return HTML formatting from the backend (e.g. multiple_product values built with <br> separators) will render with visible tags. If those types are meant to be formatted, they should be explicitly allowlisted for v-html and their backend values should be sanitized (e.g. wp_kses_post) before being returned to the SPA.

Suggested change
<div v-else-if="field.type === 'image_upload' || field.type === 'file_upload' || field.type === 'signature_field' || field.type === 'checkbox_grid' || field.type === 'multiple_choice_grid'" v-html="field.value"></div>
<div v-else-if="field.type === 'image_upload' || field.type === 'file_upload' || field.type === 'signature_field' || field.type === 'checkbox_grid' || field.type === 'multiple_choice_grid'" v-html="field.value"></div>
<div v-else-if="field.type === 'multiple_product'" v-html="field.value"></div>

Copilot uses AI. Check for mistakes.
<div v-else>{{ field.value }}</div>
</td>
</tr>
</template>
Expand Down
4 changes: 2 additions & 2 deletions assets/spa/components/component-table/template.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@
<th class="col-entry-id">
<router-link :to="{ name: 'formEntriesSingle', params: { entryid: entry.id }}">#{{ entry.id }}</router-link>
</th>
<td v-for="(header, index) in columns"><span v-html="entry.fields[index]"></span></td>
<td v-for="(header, index) in columns"><span>{{ entry.fields[index] }}</span></td>
<th class="col-entry-details">
<template v-if="status == 'trash'">
<a href="#" @click.prevent="restore(entry.id)"><?php _e( 'Restore', 'weforms' ); ?></a>
Expand All @@ -103,7 +103,7 @@
<th class="col-entry-id">
<router-link :to="{ name: 'formEntriesSingle', params: { entryid: entry.id }}">#{{ entry.id }}</router-link>
</th>
<td v-for="(header, index) in columns"><span v-html="entry.fields[index]"></span></td>
<td v-for="(header, index) in columns"><span>{{ entry.fields[index] }}</span></td>
<th class="col-entry-details">
<template v-if="status == 'trash'">
<a href="#" @click.prevent="restore(entry.id)"><?php _e( 'Restore', 'weforms' ); ?></a>
Expand Down
4 changes: 3 additions & 1 deletion assets/spa/components/form-entry-single/template.php
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
</div>
<div v-else-if="field.type === 'country_list_field'">{{ getCountryName( field.value ) }}</div>
<div v-else-if="field.type === 'address_field'" v-html="getAddressFieldValue( field.value)"></div>
<div v-else v-html="field.value"></div>
<div v-else-if="field.type === 'textarea_field'" v-html="field.value"></div>
<div v-else-if="field.type === 'image_upload' || field.type === 'file_upload' || field.type === 'signature_field' || field.type === 'checkbox_grid' || field.type === 'multiple_choice_grid'" v-html="field.value"></div>
<div v-else>{{ field.value }}</div>
</td>
</tr>
</template>
Expand Down
2 changes: 1 addition & 1 deletion includes/api/class-weforms-forms-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,7 @@ public function save_check( $request ) {
$entry_fields = [];

foreach ( $form_fields as $key => $field ) {
if ( $field['wpuf_cond']['condition_status'] == 'yes' ) {
if ( ! empty( $field['wpuf_cond'] ) && $field['wpuf_cond']['condition_status'] == 'yes' ) {
$logic = [];
$cond_fields = $field['wpuf_cond']['cond_field'];
$cond_operators = $field['wpuf_cond']['cond_operator'];
Expand Down
25 changes: 12 additions & 13 deletions includes/class-form-entry.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public function populate_entry_data() {
$this->raw_fields[ $result->meta_key ]['value'] = $value;

if ( $field['type'] == 'textarea_field' ) {
$value = weforms_format_text( $value );
$value = wp_kses_post( weforms_format_text( $value ) );
} elseif ( $field['type'] == 'name_field' ) {
$value = implode( ' ', explode( WeForms::$field_separator, $value ) );
} elseif ( in_array( $field['type'], [ 'dropdown_field', 'radio_field' ] ) ) {
Expand Down Expand Up @@ -180,16 +180,16 @@ public function populate_entry_data() {
if ( $field['type'] == 'image_upload' ) {
$thumb = wp_get_attachment_image( $attachment_id, 'thumbnail' );
} else {
$thumb = get_post_field( 'post_title', $attachment_id );
$thumb = esc_html( get_post_field( 'post_title', $attachment_id ) );
}

$full_size = wp_get_attachment_url( $attachment_id );
$full_size = esc_url( wp_get_attachment_url( $attachment_id ) );

$file_field .= sprintf( '<a href="%s" target="_blank">%s</a> ', $full_size, $thumb );
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

The generated file links use target="_blank" without a rel attribute. Add rel="noopener noreferrer" to prevent reverse-tabnabbing when admins click uploaded file links.

Suggested change
$file_field .= sprintf( '<a href="%s" target="_blank">%s</a> ', $full_size, $thumb );
$file_field .= sprintf( '<a href="%s" target="_blank" rel="noopener noreferrer">%s</a> ', $full_size, $thumb );

Copilot uses AI. Check for mistakes.
}
}

$value = $file_field;
$value = wp_kses_post( $file_field );
} elseif ( $field['type'] == 'google_map' ) {
list( $address, $lat, $long ) = explode( '||', $value );

Expand Down Expand Up @@ -251,7 +251,7 @@ public function populate_entry_data() {
<div class="wpufTableHead">&nbsp;</div>';

foreach ( $field['grid_columns'] as $column ) {
$return .= '<div class="wpufTableHead">' . $column . '</div>';
$return .= '<div class="wpufTableHead">' . esc_html( $column ) . '</div>';
}

$return .= '</div>
Expand All @@ -260,7 +260,7 @@ public function populate_entry_data() {

foreach ( $field['grid_rows'] as $row_key => $row_value ) {
$return .= '<div class="wpufTableRow">
<div class="wpufTableHead">' . $row_value . '</div>';
<div class="wpufTableHead">' . esc_html( $row_value ) . '</div>';

foreach ( $field['grid_columns'] as $column_key => $column_value ) {
if ( isset( $new_val[ $row_key ] ) ) {
Expand Down Expand Up @@ -317,7 +317,7 @@ class="wpuf_' . $field['name'] . '_' . $this->form_id . '"
<div class="wpufTableHead">&nbsp;</div>';

foreach ( $field['grid_columns'] as $column ) {
$return .= '<div class="wpufTableHead">' . $column . '</div>';
$return .= '<div class="wpufTableHead">' . esc_html( $column ) . '</div>';
}

$return .= '</div>
Expand All @@ -326,7 +326,7 @@ class="wpuf_' . $field['name'] . '_' . $this->form_id . '"

foreach ( $field['grid_rows'] as $row_key => $row_value ) {
$return .= '<div class="wpufTableRow">
<div class="wpufTableHead">' . $row_value . '</div>';
<div class="wpufTableHead">' . esc_html( $row_value ) . '</div>';

foreach ( $field['grid_columns'] as $column_key => $column_value ) {
if ( isset( $new_val[ $row_key ] ) ) {
Expand Down Expand Up @@ -373,16 +373,15 @@ class="wpuf_' . $field['name'] . '_' . $this->form_id . '"
$value = implode( '<br> ', $serialized_value );
}
} elseif ( $field['type'] == 'signature_field' ) {
$url = $value;

if ( isset( $_REQUEST['action'] ) != 'weforms_pdf_download' ) {
$url = content_url() . '/' . $value;
$url = esc_url( content_url() . '/' . $value );
$value = sprintf( '<img src="%s">', $url );
$value .= sprintf( '<a style="margin-left: -200px" href="%s">Download</a>', $url );
}
else{
} else {
Comment on lines 376 to +380
Copy link

Copilot AI Mar 2, 2026

Choose a reason for hiding this comment

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

In the signature_field rendering, the condition isset( $_REQUEST['action'] ) != 'weforms_pdf_download' compares a boolean to a string, so it will always evaluate true and the else branch is effectively unreachable. This can break the intended PDF-download behavior and URL handling. Compare the actual action value (and handle missing action) instead of using isset() in the comparison.

Copilot uses AI. Check for mistakes.
$url = esc_url( $value );
$value = sprintf( '<img src="%s">', $url );
}
$value = wp_kses_post( $value );
}

$this->fields[ $result->meta_key ]['value'] = apply_filters( 'weforms_entry_meta_field', $value, $field );
Expand Down
1 change: 0 additions & 1 deletion includes/class-form.php
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ public function maybe_update_entries( $form_fields ) {
public function get_changed_fields( $form_fields ) {
$changed_fields = array();
foreach ( $form_fields as $field ) {
$org_field = $field['original_name'];
// All form fields should have an original name.
if ( empty( $field['original_name'] ) ) {
continue;
Expand Down
1 change: 1 addition & 0 deletions includes/class-notification.php
Original file line number Diff line number Diff line change
Expand Up @@ -602,6 +602,7 @@ public static function replace_name_tag( $text, $entry_id ) {
* @return string
*/
public static function replace_file_tags( $text, $entry_id ) {
$text = $text ?? '';
$pattern = '/{(?:image|file):(\w*)}/';

preg_match_all( $pattern, $text, $matches );
Expand Down
13 changes: 9 additions & 4 deletions includes/fields/class-abstract-fields.php
Original file line number Diff line number Diff line change
Expand Up @@ -539,13 +539,18 @@ public function prepare_entry( $field, $args = [] ) {
wp_send_json_error( __( 'Unauthorized operation', 'weforms' ) );
}

$args = ! empty( $args ) ? $args : weforms_clean( $_POST );
$value = !empty( $args[$field['name']] ) ? $args[$field['name']] : '';
if ( $args instanceof WP_REST_Request ) {
$args = weforms_clean( $args->get_params() );
} elseif ( empty( $args ) ) {
$args = weforms_clean( $_POST );
}

$value = ! empty( $args[ $field['name'] ] ) ? $args[ $field['name'] ] : '';

if ( is_array( $value ) ) {
$entry_value = implode( WeForms::$field_separator, $args[$field['name']] );
$entry_value = implode( WeForms::$field_separator, $value );
} else {
$entry_value = trim( $value );
$entry_value = sanitize_textarea_field( trim( $value ) );
}

return $entry_value;
Expand Down
3 changes: 2 additions & 1 deletion includes/functions.php
Original file line number Diff line number Diff line change
Expand Up @@ -621,7 +621,7 @@ function weforms_get_form_field_labels( $form_id ) {
}

$data[ $field['name'] ] = [
'label' => $field['label'],
'label' => $field['label'] ?? '',
'type' => $field['template'],
];
}
Expand Down Expand Up @@ -715,6 +715,7 @@ function weforms_get_browser() {
$bname = 'Unknown';
$platform = 'Unknown';
$version = '';
$ub = '';

// first get the platform
if ( preg_match( '/linux/i', $u_agent ) ) {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "weForms",
"author": "BoldGrid",
"version": "1.6.27",
"version": "1.6.28",
"license": "GPL-2.0",
"repository": {
"type": "git",
Expand Down
8 changes: 7 additions & 1 deletion readme.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Tags: form builder, contact form, forms, form creator, custom form
Requires at least: 5.0
Requires PHP: 7.2.5
Tested up to: 6.9
Stable tag: 1.6.27
Stable tag: 1.6.28
License: GPLv2 or later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

Expand Down Expand Up @@ -240,6 +240,12 @@ Please report security bugs found in the source code of the undefined plugin thr

== Changelog ==

= Version 1.6.28 ( 27 February, 2026 ) =
* Security: Patched stored XSS vulnerability in form entry fields.

= Version 1.6.27 ( 09 February, 2026 ) =
* Security: Patched object injection vulnerability.

= Version 1.6.26 ( 17 December, 2025 ) =
* Fix: Added extra validation for form uploads.

Expand Down
4 changes: 2 additions & 2 deletions weforms.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* Plugin URI: https://weformspro.com/
* Author: weForms
* Author URI: https://weformspro.com/
* Version: 1.6.27
* Version: 1.6.28
* License: GPL2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: weforms
Expand Down Expand Up @@ -55,7 +55,7 @@ final class WeForms {
*
* @var string
*/
public $version = '1.6.27';
public $version = '1.6.28';

/**
* Form field value seperator
Expand Down