diff --git a/.github/workflows/ai-pr-review.yml b/.github/workflows/ai-pr-review.yml index 864448486..c4cee6e96 100644 --- a/.github/workflows/ai-pr-review.yml +++ b/.github/workflows/ai-pr-review.yml @@ -10,7 +10,7 @@ jobs: # Job-level environment env: - AI_REVIEW_MODEL: gpt-4.1-mini + AI_REVIEW_MODEL: gpt-5-mini permissions: contents: read # to read the repo diff --git a/README.md b/README.md index 700fd1d56..be78602a9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,14 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro ## ChangeLog +## 3.5.6.3 +* ADD: Support preview for AVIF files in Media Field +* ADD: `jet-form-builder/form-record/general-values-columns` filter for Form Record General Values Columns +* FIX: LFI vulnerability + +## 3.5.6.2 +* FIX: RCE vulnerability + ## 3.5.6.1 * FIX: Compatibility with jetBooking 4.0.0 diff --git a/assets/build/frontend/advanced.reporting.asset.php b/assets/build/frontend/advanced.reporting.asset.php index 286eca8a1..583b9dd92 100644 --- a/assets/build/frontend/advanced.reporting.asset.php +++ b/assets/build/frontend/advanced.reporting.asset.php @@ -1 +1 @@ - array('wp-api-fetch'), 'version' => '8ff3219219229b3acd2f'); + array('wp-api-fetch'), 'version' => 'cd9019dc6f52cd8ab6d0'); diff --git a/assets/build/frontend/advanced.reporting.js b/assets/build/frontend/advanced.reporting.js index 5e8da9790..e4b4d729d 100644 --- a/assets/build/frontend/advanced.reporting.js +++ b/assets/build/frontend/advanced.reporting.js @@ -1 +1 @@ -(()=>{"use strict";var t={n:e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return t.d(i,{a:i}),i},d:(e,i)=>{for(var r in i)t.o(i,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:i[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)};const{Restriction:e,CalculatedFormula:i}=window.JetFormBuilderAbstract;function r(){e.call(this),this.message="",this.formula=null,this.watchedAttrs=[]}r.prototype=Object.create(e.prototype),r.prototype.message="",r.prototype.formula=null,r.prototype.watchedAttrs=[],r.prototype.isServerSide=function(){return!1},r.prototype.getMessage=function(){return this.message},r.prototype.getRawMessage=function(){return"Error"},r.prototype.getMessageBySlug=function(t){return function(t,e){var i,r,s,n;const{reporting:o}=t,a=null!==(i=o.messages[e])&&void 0!==i?i:"";if(a)return a;const u=null!==(r=window?.JetFormsValidation)&&void 0!==r&&r;if(!1===u)return"";const c=o.input.getSubmit(),{messages:l}=null!==(s=u[c.getFormId()])&&void 0!==s?s:{};return null!==(n=l[e])&&void 0!==n?n:""}(this,t)},r.prototype.onReady=function(){this.formula=new i(this.reporting.input),this.formula.observe(this.getRawMessage()),this.formula.setResult=()=>{this.message=this.formula.calculateString()},this.formula.setResult(),this.watchedAttrs.length&&(this.reporting.watchAttrs=[...new Set([...this.reporting.watchAttrs,...this.watchedAttrs])])};const s=r;function n(){s.call(this),this.watchedAttrs.push("max"),this.isSupported=function(t,e){const{max:i=!1}=e.input.attrs;return!1!==i&&["number","range"].includes(t.type)},this.validate=function(){const t=this.getValue(),{max:e}=this.reporting.input.attrs;return!t||+t<=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("number_max")}}n.prototype=Object.create(s.prototype);const o=n;function a(){s.call(this),this.watchedAttrs.push("min"),this.isSupported=function(t,e){const{min:i=!1}=e.input.attrs;return!1!==i&&["number","range"].includes(t.type)},this.validate=function(){const t=this.getValue(),{min:e}=this.reporting.input.attrs;return!t||+t>=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("number_min")}}a.prototype=Object.create(s.prototype);const u=a;function c(){s.call(this),this.isSupported=function(t){return"url"===t.type},this.validate=function(){const t=this.getValue();return!t||/((mailto\:|(news|(ht|f)tp(s?))\:\/\/)\S+)/.test(t)},this.getRawMessage=function(){return this.getMessageBySlug("url")}}c.prototype=Object.create(s.prototype);const l=c;function p(){s.call(this),this.isSupported=function(t){return"email"===t.type},this.validate=function(){const t=this.getValue();return!t||/^[\w-\.\+]+@([\w-]+\.)+[\w-]{1,6}$/.test(t)},this.getRawMessage=function(){return this.getMessageBySlug("email")}}p.prototype=Object.create(s.prototype);const h=p;function d(){s.call(this),this.watchedAttrs.push("minLength"),this.isSupported=function(t,e){const{minLength:i=!1}=e.input.attrs;return!1!==i},this.validate=function(){const t=this.getValue()?.length,{minLength:e}=this.reporting.input.attrs;return!t||t>=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("char_min")}}d.prototype=Object.create(s.prototype);const g=d;function f(){s.call(this),this.watchedAttrs.push("maxLength"),this.isSupported=function(t,e){const{maxLength:i=!1}=e.input.attrs;return!1!==i},this.validate=function(){const t=this.getValue()?.length,{maxLength:e}=this.reporting.input.attrs;return!t||t<=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("char_max")}}f.prototype=Object.create(s.prototype);const m=f,{isEmpty:y}=JetFormBuilderFunctions;function v(){s.call(this),this.type="required"}v.prototype=Object.create(s.prototype),v.prototype.isSupported=function(t,e){return e.input.isRequired},v.prototype.validate=function(){const t=this.getValue();return!y(t)},v.prototype.getRawMessage=function(){return this.getMessageBySlug("empty")};const S=v;function b(){s.call(this),this.isSupported=function(t){return t.classList.contains("jet-form-builder__masked-field")&&jQuery.fn.inputmask},this.validate=function(){const t=this.getValue(),e=this.reporting.getNode();return!t||e.inputmask.isComplete()},this.getRawMessage=function(){return this.getMessageBySlug("inputmask")}}b.prototype=Object.create(s.prototype);const w=b;function j(){s.call(this),this.isSupported=function(t,e){var i;const r=t.closest(".jet-form-builder-row"),s=JSON.parse(null!==(i=r.dataset?.validationRules)&&void 0!==i?i:"[]");return!!Boolean(s.length)&&(e.restrictions=[...e.restrictions,...X(s,e)],!1)}}j.prototype=Object.create(s.prototype);const _=j;function R(){s.call(this),this.attrs={}}R.prototype=Object.create(s.prototype),R.prototype.attrs={},R.prototype.setAttrs=function(t){this.attrs=t},R.prototype.getSlug=function(){throw new Error("you need to return slug of rule")},R.prototype.getRawMessage=function(){var t;return null!==(t=this.attrs?.message)&&void 0!==t?t:""};const P=R;function A(){P.call(this),this.getSlug=function(){return"contain"}}A.prototype=Object.create(P.prototype),A.prototype.setAttrs=function(t){P.prototype.setAttrs.call(this,t),Z.call(this)},A.prototype.validate=function(){const t=this.getValue();return!t||t.includes(this.attrs.value)};const O=A;function B(){O.call(this),this.getSlug=function(){return"contain_not"},this.validate=function(){return!this.getValue()||!O.prototype.validate.call(this)}}B.prototype=Object.create(O.prototype);const V=B;function x(){P.call(this),this.getSlug=function(){return"regexp"}}x.prototype=Object.create(P.prototype),x.prototype.setAttrs=function(t){P.prototype.setAttrs.call(this,t),Z.call(this)},x.prototype.validate=function(){const t=this.getValue();return!t||new RegExp(this.attrs.value,"g").test(t)};const M=x;function F(){M.call(this),this.getSlug=function(){return"regexp_not"},this.validate=function(){return!this.getValue()||!M.prototype.validate.call(this)}}F.prototype=Object.create(M.prototype);const E=F,k=window.wp.apiFetch;var L=t.n(k);function N(){P.call(this),this.getSlug=function(){return"ssr"},this.isServerSide=function(){return!0},this.validatePromise=async function(t=null){if(!this.getValue())return Promise.resolve();const e=this.getFormData(),{rootNode:i}=this.reporting.input.getRoot();switch(i.getAttribute("ssr_validation_method")||"rest"){case"admin_ajax":return this.validateViaAdminAjax(e,t);case"self":return this.validateViaSelfRequest(e,t);default:return this.validateViaRest(e,t)}},this.validateViaRest=async function(t,e){try{const i=await L()({path:"/jet-form-builder/v1/validate-field",method:"POST",body:t,signal:e});return i?.result?Promise.resolve():Promise.reject()}catch(t){throw t}},this.validateViaAdminAjax=async function(t,e){try{const i=await fetch(window.JetFormBuilderSettings.adminajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"jet_fb_ssr_validation_ajax",data:JSON.stringify(Object.fromEntries(t))}),signal:e}).then((t=>t.json()));return i?.result?Promise.resolve():Promise.reject()}catch(t){throw t}},this.validateViaSelfRequest=async function(t,e){try{const i=new URL(window.location.href);i.searchParams.set("jet_fb_ssr_self_validation","1");for(const[e,r]of t.entries())i.searchParams.append(e,r);const r=await fetch(i.toString(),{method:"GET",signal:e}).then((t=>t.json()));return r?.result?Promise.resolve():Promise.reject()}catch(t){throw t}},this.getFormData=function(){const{input:t}=this.reporting,{rootNode:e}=t.getRoot(),i=new FormData(e);i.delete("_wpnonce"),i.set("_jfb_validation_rule_index",this.attrs.index);for(const e of t.path)i.append("_jfb_validation_path[]",e);return i}}N.prototype=Object.create(P.prototype);const J=N;function T(){P.call(this),this.getSlug=function(){return"equal"},this.validate=function(){const t=this.getValue();return!t||t===this.attrs.value}}T.prototype=Object.create(P.prototype),T.prototype.setAttrs=function(t){P.prototype.setAttrs.call(this,t),Z.call(this)};const C=T,{getTimestamp:q}=JetFormBuilderFunctions;function I(){s.call(this),this.watchedAttrs.push("min"),this.isSupported=function(t,e){const{min:i=!1}=e.input.attrs;return!1!==i&&["time","date","datetime-local"].includes(t.type)},this.validate=function(){const t=this.getValue();if(!t)return!0;const{min:e}=this.reporting.input.attrs,{time:i}=q(t),{time:r}=q(e.value.current);return i>=r},this.getRawMessage=function(){return this.getMessageBySlug("date_min")}}I.prototype=Object.create(s.prototype);const D=I,{getTimestamp:H}=JetFormBuilderFunctions;function U(){s.call(this),this.watchedAttrs.push("max"),this.isSupported=function(t,e){const{max:i=!1}=e.input.attrs;return!1!==i&&["time","date","datetime-local"].includes(t.type)},this.validate=function(){const t=this.getValue();if(!t)return!0;const{max:e}=this.reporting.input.attrs,{time:i}=H(t),{time:r}=H(e.value.current);return i<=r},this.getRawMessage=function(){return this.getMessageBySlug("date_max")}}U.prototype=Object.create(s.prototype);const G=U,{applyFilters:Q}=JetPlugins.hooks,{isEmpty:$}=JetFormBuilderFunctions,z=()=>Q("jet.fb.advanced.rules",[O,V,M,E,J,C]);let K=[],W=[];function X(t,e){const i=[];K.length||(K=z());for(const[r,s]of Object.entries(t))for(const t of K){const n=new t;if(s.type===n.getSlug()){delete s.type,n.setReporting(e),n.setAttrs({...s,index:r}),n.onReady(),i.push(n);break}}return i}function Y(t){return t.closest(".jet-form-builder-row")}function Z(){if(!this.attrs?.field)return;const{root:t}=this.reporting.input,e=t.getInput(this.attrs.field);e.watch((()=>{this.attrs.value=e.value.current,this.reporting.valuePrev=null,this.reporting.validateOnChange()})),$(e.value.current)||(this.attrs.value=e.value.current)}function tt(t){for(const e of t.children)if(e.classList.contains("error-message"))return e;const e=t.querySelector(".jet-form-builder-col__end");return!!e&&tt(e)}const{ReportingInterface:et}=JetFormBuilderAbstract,{allRejected:it}=JetFormBuilderFunctions;function rt(){et.call(this),this.type="inherit",this.messages={},this.skipServerSide=!0,this.watchAttrs=[],this.queue=[]}rt.prototype=Object.create(et.prototype),rt.prototype.skipServerSide=!0,rt.prototype.hasServerSide=!1,rt.prototype.isProcess=null,rt.prototype.queue=[],rt.prototype.setRestrictions=function(){!function(t){W.length||(W=Q("jet.fb.restrictions",[D,G,o,u,l,h,w,g,m,S,_]));for(const e of W){const i=new e;i.isSupported(t.getNode(),t)&&(i.setReporting(t),i.onReady(),t.restrictions.push(i))}}(this)},rt.prototype.isSupported=function(t,e){this.type=function(t){var e;const i=Y(t),{validationType:r=""}=null!==(e=i?.dataset)&&void 0!==e?e:{};return r}(t);const i="inherit"===this.type?function(t){var e,i;const r=null!==(e=window?.JetFormsValidation)&&void 0!==e&&e;if(!1===r)return"";const s=t.getSubmit().getFormId(),{type:n=""}=null!==(i=r[s])&&void 0!==i?i:{};return n}(e):this.type;return!!i?.length},rt.prototype.getErrorsRaw=async function(t,e=null){this.hasServerSide&&this.input.loading.start();let i=await it(t);return this.valuePrev=this.input.getValue(),this.hasServerSide&&this.input.loading.end(),e?.aborted&&(i=[]),i},rt.prototype.reportRaw=function(t){let e="";for(const i of t)if(e=i.getMessage(),e?.length)break;e?this.insertError(e):this.clearReport()},rt.prototype.setInput=function(t){this.messages=function(t){var e;const i=Y(t),{validationMessages:r="{}"}=null!==(e=i?.dataset)&&void 0!==e?e:{};return JSON.parse(r)}(t.nodes[0]),et.prototype.setInput.call(this,t)},rt.prototype.observeAttrs=function(){for(const t of this.watchAttrs)this.input.attrs.hasOwnProperty(t)&&this.input.attrs[t].value.watch((()=>{this.valuePrev=null,this.validateOnBlur()}))},rt.prototype.clearReport=function(){(()=>{const t=Y(this.getNode());if(!t)return;t.classList.remove("field-has-error");const e=tt(t);e&&e.remove()})(),this.makeValid()},rt.prototype.insertError=function(t){(()=>{const e=Y(this.getNode()),i=this.createError(e,t);if(e.classList.add("field-has-error"),i.isConnected)return;const r=e.querySelector(".jet-form-builder-col__end");r?r.appendChild(i):e.appendChild(i)})(),this.makeInvalid()},rt.prototype.createError=function(t,e){const i=tt(t);if(i)return i.innerHTML=e,i;const r=this.getNode(),s=document.createElement("div");return s.classList.add("error-message"),s.innerHTML=e,s.id=r.id+"__error",s},rt.prototype.makeInvalid=function(){var t;const e=tt(Y(this.getNode()));this.getNode().setAttribute("aria-invalid","true"),this.getNode().setAttribute("aria-describedby",null!==(t=e?.id)&&void 0!==t&&t)},rt.prototype.makeValid=function(){this.getNode().removeAttribute("aria-invalid"),this.getNode().removeAttribute("aria-describedby")},rt.prototype.validateOnChange=function(t=!1){const e=this.input.getValue();if((!e||""===e)&&null===this.valuePrev&&this.input.getContext().silence)return;this.switchButtonsState(!0);const i=()=>{this.input.getContext().setSilence(!1),this.validate().then((()=>{})).catch((()=>{})).finally((()=>{this.isProcess=null;const t=[...this.queue];this.queue=[],t.length?(this.valuePrev=null,t.forEach((t=>t())),this.switchButtonsState()):this.switchButtonsState()}))};t&&this.isProcess&&(this.queue=[i]),this.isProcess||(this.isProcess=!0,i())},rt.prototype.validateOnBlur=function(t=null){if(this.isProcess)return;const e=this.input.getValue();(e&&""!==e||null!==this.valuePrev||!this.input.getContext().silence)&&(this.isProcess=!0,this.skipServerSide=!1,this.switchButtonsState(!0),this.canSubmitForm(!1),this.canTriggerEnterSubmit(!1),this.input.getContext().setSilence(!1),this.validate(t).then((()=>{})).catch((()=>{})).finally((()=>{this.skipServerSide=!0,this.hasServerSide=!1,this.isProcess=null,this.input.nodes[0].readOnly=!1,t?.aborted||(this.switchButtonsState(),this.canSubmitForm(),this.validityState.current&&this.canTriggerEnterSubmit())})))},rt.prototype.switchButtonsState=function(t=!1){const e=this.input.nodes[0].closest(".jet-form-builder-page");if(e&&!this.input.getContext().silence){const i=e.querySelectorAll(".jet-form-builder__next-page, .jet-form-builder__prev-page, .jet-form-builder__action-button");for(const e of i)(e.classList.contains("jet-form-builder__submit")||this.isNodeBelongThis(e))&&(e.classList.contains("jet-form-builder__prev-page")?e.disabled=t:e.disabled=!0===t||!this.validityState.current)}},rt.prototype.canTriggerEnterSubmit=function(t=!0){const e=this.input.root.form;e&&(e.canTriggerEnterSubmit=t)},rt.prototype.canSubmitForm=function(t=!0){const e=this.input.root.form;e&&(e.canSubmitForm=t)},rt.prototype.isNodeBelongThis=function(t){const e=t.closest(".jet-form-builder-page");return!!e&&!e.classList.contains("jet-form-builder-page--hidden")},rt.prototype.validateOnChangeState=function(){if(this.isProcess)return Promise.resolve();const t=this.input.getValue();return t&&""!==t||null!==this.valuePrev||!this.input.getContext().silence?(this.switchButtonsState(!0),this.canTriggerEnterSubmit(!1),this.input.maskValidation&&this.input.changeStateMaskValidation(),this.isProcess=!0,this.skipServerSide=!1,new Promise(((t,e)=>{this.validate().then(t).catch(e).finally((()=>{this.skipServerSide=!0,this.hasServerSide=!1,this.isProcess=null,this.input.nodes[0].readOnly=!1,this.switchButtonsState(),this.canTriggerEnterSubmit()}))}))):Promise.resolve()},rt.prototype.canProcessRestriction=function(t){return!this.skipServerSide||!t.isServerSide()},rt.prototype.beforeProcessRestriction=function(t){this.hasServerSide=!!t.isServerSide()||this.hasServerSide};const st=rt;var nt;const{addFilter:ot,addAction:at}=JetPlugins.hooks;at("jet.fb.observe.after","jet-form-builder/observe-dynamic-attrs",(function(t){t.getInputs().forEach((t=>{t.reporting instanceof st&&t.reporting.observeAttrs()}))}),11),ot("jet.fb.reporting","jet-form-builder/advanced-reporting",(function(t){return[st,...t]})),window.JetFormBuilderAbstract={...null!==(nt=window.JetFormBuilderAbstract)&&void 0!==nt?nt:{},AdvancedRestriction:s,NotEmptyRestriction:S}})(); \ No newline at end of file +(()=>{"use strict";var t={n:e=>{var i=e&&e.__esModule?()=>e.default:()=>e;return t.d(i,{a:i}),i},d:(e,i)=>{for(var r in i)t.o(i,r)&&!t.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:i[r]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)};const{Restriction:e,CalculatedFormula:i}=window.JetFormBuilderAbstract;function r(){e.call(this),this.message="",this.formula=null,this.watchedAttrs=[]}r.prototype=Object.create(e.prototype),r.prototype.message="",r.prototype.formula=null,r.prototype.watchedAttrs=[],r.prototype.isServerSide=function(){return!1},r.prototype.getMessage=function(){return this.message},r.prototype.getRawMessage=function(){return"Error"},r.prototype.getMessageBySlug=function(t){return function(t,e){var i,r,s,n;const{reporting:o}=t,a=null!==(i=o.messages[e])&&void 0!==i?i:"";if(a)return a;const u=null!==(r=window?.JetFormsValidation)&&void 0!==r&&r;if(!1===u)return"";const c=o.input.getSubmit(),{messages:l}=null!==(s=u[c.getFormId()])&&void 0!==s?s:{};return null!==(n=l[e])&&void 0!==n?n:""}(this,t)},r.prototype.onReady=function(){this.formula=new i(this.reporting.input),this.formula.observe(this.getRawMessage()),this.formula.setResult=()=>{this.message=this.formula.calculateString()},this.formula.setResult(),this.watchedAttrs.length&&(this.reporting.watchAttrs=[...new Set([...this.reporting.watchAttrs,...this.watchedAttrs])])};const s=r;function n(){s.call(this),this.watchedAttrs.push("max"),this.isSupported=function(t,e){const{max:i=!1}=e.input.attrs;return!1!==i&&["number","range"].includes(t.type)},this.validate=function(){const t=this.getValue(),{max:e}=this.reporting.input.attrs;return!t||+t<=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("number_max")}}n.prototype=Object.create(s.prototype);const o=n;function a(){s.call(this),this.watchedAttrs.push("min"),this.isSupported=function(t,e){const{min:i=!1}=e.input.attrs;return!1!==i&&["number","range"].includes(t.type)},this.validate=function(){const t=this.getValue(),{min:e}=this.reporting.input.attrs;return!t||+t>=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("number_min")}}a.prototype=Object.create(s.prototype);const u=a;function c(){s.call(this),this.isSupported=function(t){return"url"===t.type},this.validate=function(){const t=this.getValue();return!t||/((mailto\:|(news|(ht|f)tp(s?))\:\/\/)\S+)/.test(t)},this.getRawMessage=function(){return this.getMessageBySlug("url")}}c.prototype=Object.create(s.prototype);const l=c;function h(){s.call(this),this.isSupported=function(t){return"email"===t.type},this.validate=function(){const t=this.getValue();return!t||/^[\w-\.\+]+@([\w-]+\.)+[\w-]{1,6}$/.test(t)},this.getRawMessage=function(){return this.getMessageBySlug("email")}}h.prototype=Object.create(s.prototype);const p=h;function d(){s.call(this),this.watchedAttrs.push("minLength"),this.isSupported=function(t,e){const{minLength:i=!1}=e.input.attrs;return!1!==i},this.validate=function(){const t=this.getValue()?.length,{minLength:e}=this.reporting.input.attrs;return!t||t>=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("char_min")}}d.prototype=Object.create(s.prototype);const g=d;function f(){s.call(this),this.watchedAttrs.push("maxLength"),this.isSupported=function(t,e){const{maxLength:i=!1}=e.input.attrs;return!1!==i},this.validate=function(){const t=this.getValue()?.length,{maxLength:e}=this.reporting.input.attrs;return!t||t<=+e.value.current},this.getRawMessage=function(){return this.getMessageBySlug("char_max")}}f.prototype=Object.create(s.prototype);const m=f,{isEmpty:y}=JetFormBuilderFunctions;function v(){s.call(this),this.type="required"}v.prototype=Object.create(s.prototype),v.prototype.isSupported=function(t,e){return e.input.isRequired},v.prototype.validate=function(){const t=this.getValue();return!y(t)},v.prototype.getRawMessage=function(){return this.getMessageBySlug("empty")};const S=v;function b(){s.call(this),this.isSupported=function(t){return t.classList.contains("jet-form-builder__masked-field")&&jQuery.fn.inputmask},this.validate=function(){const t=this.getValue(),e=this.reporting.getNode();return!t||e.inputmask.isComplete()},this.getRawMessage=function(){return this.getMessageBySlug("inputmask")}}b.prototype=Object.create(s.prototype);const w=b;function j(){s.call(this),this.isSupported=function(t,e){var i;const r=t.closest(".jet-form-builder-row"),s=JSON.parse(null!==(i=r.dataset?.validationRules)&&void 0!==i?i:"[]");return!!Boolean(s.length)&&(e.restrictions=[...e.restrictions,...X(s,e)],!1)}}j.prototype=Object.create(s.prototype);const _=j;function R(){s.call(this),this.attrs={}}R.prototype=Object.create(s.prototype),R.prototype.attrs={},R.prototype.setAttrs=function(t){this.attrs=t},R.prototype.getSlug=function(){throw new Error("you need to return slug of rule")},R.prototype.getRawMessage=function(){var t;return null!==(t=this.attrs?.message)&&void 0!==t?t:""};const P=R;function A(){P.call(this),this.getSlug=function(){return"contain"}}A.prototype=Object.create(P.prototype),A.prototype.setAttrs=function(t){P.prototype.setAttrs.call(this,t),Z.call(this)},A.prototype.validate=function(){const t=this.getValue();return!t||t.includes(this.attrs.value)};const O=A;function B(){O.call(this),this.getSlug=function(){return"contain_not"},this.validate=function(){return!this.getValue()||!O.prototype.validate.call(this)}}B.prototype=Object.create(O.prototype);const V=B;function x(){P.call(this),this.getSlug=function(){return"regexp"}}x.prototype=Object.create(P.prototype),x.prototype.setAttrs=function(t){P.prototype.setAttrs.call(this,t),Z.call(this)},x.prototype.validate=function(){const t=this.getValue();return!t||new RegExp(this.attrs.value,"g").test(t)};const M=x;function F(){M.call(this),this.getSlug=function(){return"regexp_not"},this.validate=function(){return!this.getValue()||!M.prototype.validate.call(this)}}F.prototype=Object.create(M.prototype);const E=F,k=window.wp.apiFetch;var L=t.n(k);function N(){P.call(this),this.getSlug=function(){return"ssr"},this.isServerSide=function(){return!0},this.validatePromise=async function(t=null){if(!this.getValue())return Promise.resolve();const e=this.getFormData(),{rootNode:i}=this.reporting.input.getRoot();switch(i.getAttribute("ssr_validation_method")||"rest"){case"admin_ajax":return this.validateViaAdminAjax(e,t);case"self":return this.validateViaSelfRequest(e,t);default:return this.validateViaRest(e,t)}},this.validateViaRest=async function(t,e){try{const i=await L()({path:"/jet-form-builder/v1/validate-field",method:"POST",body:t,signal:e});return i?.result?Promise.resolve():Promise.reject()}catch(t){throw t}},this.validateViaAdminAjax=async function(t,e){try{const i=await fetch(window.JetFormBuilderSettings.adminajaxurl,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({action:"jet_fb_ssr_validation_ajax",data:JSON.stringify(Object.fromEntries(t))}),signal:e}).then((t=>t.json()));return i?.result?Promise.resolve():Promise.reject()}catch(t){throw t}},this.validateViaSelfRequest=async function(t,e){try{const i=new URL(window.location.href);i.searchParams.set("jet_fb_ssr_self_validation","1");for(const[e,r]of t.entries())i.searchParams.append(e,r);const r=await fetch(i.toString(),{method:"GET",signal:e}).then((t=>t.json()));return r?.result?Promise.resolve():Promise.reject()}catch(t){throw t}},this.getFormData=function(){const{input:t}=this.reporting,{rootNode:e}=t.getRoot(),i=new FormData(e);i.delete("_wpnonce"),i.set("_jfb_validation_rule_index",this.attrs.index);for(const e of t.path)i.append("_jfb_validation_path[]",e);return this.attrs._sig&&i.set("_jfb_validation_sig",this.attrs._sig),i}}N.prototype=Object.create(P.prototype);const J=N;function T(){P.call(this),this.getSlug=function(){return"equal"},this.validate=function(){const t=this.getValue();return!t||t===this.attrs.value}}T.prototype=Object.create(P.prototype),T.prototype.setAttrs=function(t){P.prototype.setAttrs.call(this,t),Z.call(this)};const C=T,{getTimestamp:q}=JetFormBuilderFunctions;function I(){s.call(this),this.watchedAttrs.push("min"),this.isSupported=function(t,e){const{min:i=!1}=e.input.attrs;return!1!==i&&["time","date","datetime-local"].includes(t.type)},this.validate=function(){const t=this.getValue();if(!t)return!0;const{min:e}=this.reporting.input.attrs,{time:i}=q(t),{time:r}=q(e.value.current);return i>=r},this.getRawMessage=function(){return this.getMessageBySlug("date_min")}}I.prototype=Object.create(s.prototype);const D=I,{getTimestamp:H}=JetFormBuilderFunctions;function U(){s.call(this),this.watchedAttrs.push("max"),this.isSupported=function(t,e){const{max:i=!1}=e.input.attrs;return!1!==i&&["time","date","datetime-local"].includes(t.type)},this.validate=function(){const t=this.getValue();if(!t)return!0;const{max:e}=this.reporting.input.attrs,{time:i}=H(t),{time:r}=H(e.value.current);return i<=r},this.getRawMessage=function(){return this.getMessageBySlug("date_max")}}U.prototype=Object.create(s.prototype);const G=U,{applyFilters:Q}=JetPlugins.hooks,{isEmpty:$}=JetFormBuilderFunctions,z=()=>Q("jet.fb.advanced.rules",[O,V,M,E,J,C]);let K=[],W=[];function X(t,e){const i=[];K.length||(K=z());for(const[r,s]of Object.entries(t))for(const t of K){const n=new t;if(s.type===n.getSlug()){delete s.type,n.setReporting(e),n.setAttrs({...s,index:r}),n.onReady(),i.push(n);break}}return i}function Y(t){return t.closest(".jet-form-builder-row")}function Z(){if(!this.attrs?.field)return;const{root:t}=this.reporting.input,e=t.getInput(this.attrs.field);e.watch((()=>{this.attrs.value=e.value.current,this.reporting.valuePrev=null,this.reporting.validateOnChange()})),$(e.value.current)||(this.attrs.value=e.value.current)}function tt(t){for(const e of t.children)if(e.classList.contains("error-message"))return e;const e=t.querySelector(".jet-form-builder-col__end");return!!e&&tt(e)}const{ReportingInterface:et}=JetFormBuilderAbstract,{allRejected:it}=JetFormBuilderFunctions;function rt(){et.call(this),this.type="inherit",this.messages={},this.skipServerSide=!0,this.watchAttrs=[],this.queue=[]}rt.prototype=Object.create(et.prototype),rt.prototype.skipServerSide=!0,rt.prototype.hasServerSide=!1,rt.prototype.isProcess=null,rt.prototype.queue=[],rt.prototype.setRestrictions=function(){!function(t){W.length||(W=Q("jet.fb.restrictions",[D,G,o,u,l,p,w,g,m,S,_]));for(const e of W){const i=new e;i.isSupported(t.getNode(),t)&&(i.setReporting(t),i.onReady(),t.restrictions.push(i))}}(this)},rt.prototype.isSupported=function(t,e){this.type=function(t){var e;const i=Y(t),{validationType:r=""}=null!==(e=i?.dataset)&&void 0!==e?e:{};return r}(t);const i="inherit"===this.type?function(t){var e,i;const r=null!==(e=window?.JetFormsValidation)&&void 0!==e&&e;if(!1===r)return"";const s=t.getSubmit().getFormId(),{type:n=""}=null!==(i=r[s])&&void 0!==i?i:{};return n}(e):this.type;return!!i?.length},rt.prototype.getErrorsRaw=async function(t,e=null){this.hasServerSide&&this.input.loading.start();let i=await it(t);return this.valuePrev=this.input.getValue(),this.hasServerSide&&this.input.loading.end(),e?.aborted&&(i=[]),i},rt.prototype.reportRaw=function(t){let e="";for(const i of t)if(e=i.getMessage(),e?.length)break;e?this.insertError(e):this.clearReport()},rt.prototype.setInput=function(t){this.messages=function(t){var e;const i=Y(t),{validationMessages:r="{}"}=null!==(e=i?.dataset)&&void 0!==e?e:{};return JSON.parse(r)}(t.nodes[0]),et.prototype.setInput.call(this,t)},rt.prototype.observeAttrs=function(){for(const t of this.watchAttrs)this.input.attrs.hasOwnProperty(t)&&this.input.attrs[t].value.watch((()=>{this.valuePrev=null,this.validateOnBlur()}))},rt.prototype.clearReport=function(){(()=>{const t=Y(this.getNode());if(!t)return;t.classList.remove("field-has-error");const e=tt(t);e&&e.remove()})(),this.makeValid()},rt.prototype.insertError=function(t){(()=>{const e=Y(this.getNode()),i=this.createError(e,t);if(e.classList.add("field-has-error"),i.isConnected)return;const r=e.querySelector(".jet-form-builder-col__end");r?r.appendChild(i):e.appendChild(i)})(),this.makeInvalid()},rt.prototype.createError=function(t,e){const i=tt(t);if(i)return i.innerHTML=e,i;const r=this.getNode(),s=document.createElement("div");return s.classList.add("error-message"),s.innerHTML=e,s.id=r.id+"__error",s},rt.prototype.makeInvalid=function(){var t;const e=tt(Y(this.getNode()));this.getNode().setAttribute("aria-invalid","true"),this.getNode().setAttribute("aria-describedby",null!==(t=e?.id)&&void 0!==t&&t)},rt.prototype.makeValid=function(){this.getNode().removeAttribute("aria-invalid"),this.getNode().removeAttribute("aria-describedby")},rt.prototype.validateOnChange=function(t=!1){const e=this.input.getValue();if((!e||""===e)&&null===this.valuePrev&&this.input.getContext().silence)return;this.switchButtonsState(!0);const i=()=>{this.input.getContext().setSilence(!1),this.validate().then((()=>{})).catch((()=>{})).finally((()=>{this.isProcess=null;const t=[...this.queue];this.queue=[],t.length?(this.valuePrev=null,t.forEach((t=>t())),this.switchButtonsState()):this.switchButtonsState()}))};t&&this.isProcess&&(this.queue=[i]),this.isProcess||(this.isProcess=!0,i())},rt.prototype.validateOnBlur=function(t=null){if(this.isProcess)return;const e=this.input.getValue();(e&&""!==e||null!==this.valuePrev||!this.input.getContext().silence)&&(this.isProcess=!0,this.skipServerSide=!1,this.switchButtonsState(!0),this.canSubmitForm(!1),this.canTriggerEnterSubmit(!1),this.input.getContext().setSilence(!1),this.validate(t).then((()=>{})).catch((()=>{})).finally((()=>{this.skipServerSide=!0,this.hasServerSide=!1,this.isProcess=null,this.input.nodes[0].readOnly=!1,t?.aborted||(this.switchButtonsState(),this.canSubmitForm(),this.validityState.current&&this.canTriggerEnterSubmit())})))},rt.prototype.switchButtonsState=function(t=!1){const e=this.input.nodes[0].closest(".jet-form-builder-page");if(e&&!this.input.getContext().silence){const i=e.querySelectorAll(".jet-form-builder__next-page, .jet-form-builder__prev-page, .jet-form-builder__action-button");for(const e of i)(e.classList.contains("jet-form-builder__submit")||this.isNodeBelongThis(e))&&(e.classList.contains("jet-form-builder__prev-page")?e.disabled=t:e.disabled=!0===t||!this.validityState.current)}},rt.prototype.canTriggerEnterSubmit=function(t=!0){const e=this.input.root.form;e&&(e.canTriggerEnterSubmit=t)},rt.prototype.canSubmitForm=function(t=!0){const e=this.input.root.form;e&&(e.canSubmitForm=t)},rt.prototype.isNodeBelongThis=function(t){const e=t.closest(".jet-form-builder-page");return!!e&&!e.classList.contains("jet-form-builder-page--hidden")},rt.prototype.validateOnChangeState=function(){if(this.isProcess)return Promise.resolve();const t=this.input.getValue();return t&&""!==t||null!==this.valuePrev||!this.input.getContext().silence?(this.switchButtonsState(!0),this.canTriggerEnterSubmit(!1),this.input.maskValidation&&this.input.changeStateMaskValidation(),this.isProcess=!0,this.skipServerSide=!1,new Promise(((t,e)=>{this.validate().then(t).catch(e).finally((()=>{this.skipServerSide=!0,this.hasServerSide=!1,this.isProcess=null,this.input.nodes[0].readOnly=!1,this.switchButtonsState(),this.canTriggerEnterSubmit()}))}))):Promise.resolve()},rt.prototype.canProcessRestriction=function(t){return!this.skipServerSide||!t.isServerSide()},rt.prototype.beforeProcessRestriction=function(t){this.hasServerSide=!!t.isServerSide()||this.hasServerSide};const st=rt;var nt;const{addFilter:ot,addAction:at}=JetPlugins.hooks;at("jet.fb.observe.after","jet-form-builder/observe-dynamic-attrs",(function(t){t.getInputs().forEach((t=>{t.reporting instanceof st&&t.reporting.observeAttrs()}))}),11),ot("jet.fb.reporting","jet-form-builder/advanced-reporting",(function(t){return[st,...t]})),window.JetFormBuilderAbstract={...null!==(nt=window.JetFormBuilderAbstract)&&void 0!==nt?nt:{},AdvancedRestriction:s,NotEmptyRestriction:S}})(); \ No newline at end of file diff --git a/assets/src/frontend/advanced.reporting/restrictions/ServerSideCallback.js b/assets/src/frontend/advanced.reporting/restrictions/ServerSideCallback.js index 9fcf3682c..6659a1cab 100644 --- a/assets/src/frontend/advanced.reporting/restrictions/ServerSideCallback.js +++ b/assets/src/frontend/advanced.reporting/restrictions/ServerSideCallback.js @@ -98,6 +98,11 @@ function ServerSideCallback() { formData.append( '_jfb_validation_path[]', pathElement ); } + // Security: Include signature for SSR validation + if ( this.attrs._sig ) { + formData.set( '_jfb_validation_sig', this.attrs._sig ); + } + return formData; }; diff --git a/includes/blocks/render/media-field-render.php b/includes/blocks/render/media-field-render.php index 10bd089cb..736cb9aac 100644 --- a/includes/blocks/render/media-field-render.php +++ b/includes/blocks/render/media-field-render.php @@ -75,7 +75,7 @@ protected function render_previews(): string { // preset field $updated = str_replace( '', $this->get_field_preset( $file ), $updated ); - $image_ext = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'svg', 'webp' ); + $image_ext = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'svg', 'webp', 'avif' ); $img_ext_preg = '!\.(' . join( '|', $image_ext ) . ')$!i'; if ( preg_match( $img_ext_preg, $file['url'] ) ) { diff --git a/includes/classes/resources/file-tools.php b/includes/classes/resources/file-tools.php index 683a7e933..ab96b9ed0 100644 --- a/includes/classes/resources/file-tools.php +++ b/includes/classes/resources/file-tools.php @@ -30,7 +30,13 @@ public static function get_uploaded( File $file, $preset ) { } protected static function is_same_file( File $file, Uploaded_File $uploaded_file ): bool { - $info = pathinfo( $uploaded_file->get_url() ); + $preset_path = $uploaded_file->get_attachment_file(); + + if ( ! $preset_path ) { + return false; + } + + $info = pathinfo( $preset_path ); return $file->get_name() === ( $info['basename'] ?? '' ); } diff --git a/includes/classes/resources/uploaded-file.php b/includes/classes/resources/uploaded-file.php index aee014116..6d5a192f7 100644 --- a/includes/classes/resources/uploaded-file.php +++ b/includes/classes/resources/uploaded-file.php @@ -96,17 +96,17 @@ public function add_attachment() { public function set_from_array( array $upload ): Uploaded_File { if ( isset( $upload['file'] ) ) { - $this->file = $upload['file']; + $this->file = self::normalize_allowed_upload_file_path( (string) $upload['file'] ); } if ( isset( $upload['url'] ) ) { - $this->url = $upload['url']; + $this->url = esc_url_raw( (string) $upload['url'] ); } if ( isset( $upload['type'] ) ) { - $this->type = $upload['type']; + $this->type = sanitize_mime_type( (string) $upload['type'] ); } if ( isset( $upload['id'] ) ) { - $this->set_attachment_id( (string) $upload['id'] ); + $this->set_attachment_id( (string) absint( $upload['id'] ) ); } return $this; @@ -185,7 +185,10 @@ public function get_attachment_file(): string { $file = $this->get_file(); if ( $file ) { - return $file; + $file = self::normalize_allowed_upload_file_path( $file ); + if ( $file ) { + return $file; + } } $id = $this->get_attachment_id(); @@ -197,13 +200,59 @@ public function get_attachment_file(): string { $file = get_attached_file( $id ); - return is_string( $file ) ? $file : ''; + if ( ! is_string( $file ) ) { + return ''; + } + + return self::normalize_allowed_upload_file_path( $file ); } /** * @param string $url */ public function set_url( string $url ) { - $this->url = $url; + $this->url = esc_url_raw( $url ); + } + + /** + * Normalize path and allow only existing files inside wp-content uploads directory. + * + * @return string Normalized realpath to a file in uploads, or empty string. + */ + public static function normalize_allowed_upload_file_path( string $file ): string { + if ( '' === $file ) { + return ''; + } + + $path = wp_normalize_path( $file ); + $real = realpath( $path ); + + if ( false === $real ) { + return ''; + } + + $real = wp_normalize_path( $real ); + $real = untrailingslashit( $real ); + + $uploads = wp_get_upload_dir(); + $base = (string) ( $uploads['basedir'] ?? '' ); + + if ( '' === $base ) { + return ''; + } + + $base_real = realpath( $base ); + if ( false === $base_real ) { + return ''; + } + + $base = wp_normalize_path( $base_real ); + $base = untrailingslashit( $base ); + + if ( 0 === strpos( $real, $base . '/' ) && is_file( $real ) ) { + return $real; + } + + return ''; } } diff --git a/jet-form-builder.php b/jet-form-builder.php index 040b1a723..915cb0c2b 100644 --- a/jet-form-builder.php +++ b/jet-form-builder.php @@ -3,7 +3,7 @@ * Plugin Name: JetFormBuilder * Plugin URI: https://jetformbuilder.com/ * Description: Advanced form builder plugin for WordPress block editor. Create forms from the ground up, customize the existing ones, and style them up – all in one editor. - * Version: 3.5.6.1 + * Version: 3.5.6.3 * Author: Crocoblock * Author URI: https://crocoblock.com/ * Text Domain: jet-form-builder @@ -18,7 +18,7 @@ die(); } -const JET_FORM_BUILDER_VERSION = '3.5.6.1'; +const JET_FORM_BUILDER_VERSION = '3.5.6.3'; const JET_FORM_BUILDER__FILE__ = __FILE__; const JET_FORM_BUILDER_SITE = 'https://jetformbuilder.com'; diff --git a/modules/actions-v2/send-email/send-email-action.php b/modules/actions-v2/send-email/send-email-action.php index 08ba5bf85..a96e050ef 100644 --- a/modules/actions-v2/send-email/send-email-action.php +++ b/modules/actions-v2/send-email/send-email-action.php @@ -5,6 +5,7 @@ use Jet_Form_Builder\Actions\Action_Handler; use Jet_Form_Builder\Actions\Types\Base; use Jet_Form_Builder\Classes\Http\Http_Tools; +use Jet_Form_Builder\Classes\Resources\Uploaded_File; use Jet_Form_Builder\Classes\Tools; use Jet_Form_Builder\Exceptions\Action_Exception; use Jet_Form_Builder\Request\Request_Tools; @@ -397,7 +398,29 @@ public function get_default_attachments(): array { ); } - return $attachments; + return $this->filter_safe_attachments( $attachments ); + } + + /** + * Allow only readable files within the uploads directory. + */ + private function filter_safe_attachments( array $attachments ): array { + $safe = array(); + + foreach ( $attachments as $attachment ) { + if ( ! is_string( $attachment ) || '' === $attachment ) { + continue; + } + + $allowed = Uploaded_File::normalize_allowed_upload_file_path( $attachment ); + if ( '' === $allowed || ! is_file( $allowed ) || ! is_readable( $allowed ) ) { + continue; + } + + $safe[] = $allowed; + } + + return array_values( array_unique( $safe ) ); } public function update_headers() { diff --git a/modules/form-record/admin/meta-boxes/form-record-values-box.php b/modules/form-record/admin/meta-boxes/form-record-values-box.php index 26ecf7d82..b9033571d 100644 --- a/modules/form-record/admin/meta-boxes/form-record-values-box.php +++ b/modules/form-record/admin/meta-boxes/form-record-values-box.php @@ -37,15 +37,19 @@ public function get_dependencies(): array { } public function get_columns(): array { - return array( - 'form' => new Form_Link_Column(), - 'referrer' => new Referrer_Link_Column(), - 'status' => new Status_Column(), - 'user' => new User_Login_Column(), - 'ip_address' => new Ip_Address_Column(), - 'user_agent' => new User_Agent_Column(), - 'created_at' => new Created_At_Column(), - 'updated_at' => new Updated_At_Column(), + return apply_filters( + 'jet-form-builder/form-record/general-values-columns', + array( + 'form' => new Form_Link_Column(), + 'referrer' => new Referrer_Link_Column(), + 'status' => new Status_Column(), + 'user' => new User_Login_Column(), + 'ip_address' => new Ip_Address_Column(), + 'user_agent' => new User_Agent_Column(), + 'created_at' => new Created_At_Column(), + 'updated_at' => new Updated_At_Column(), + ), + $this ); } diff --git a/modules/form-record/export/multiple-controller.php b/modules/form-record/export/multiple-controller.php index 6c45a8ea7..4a83bf6af 100644 --- a/modules/form-record/export/multiple-controller.php +++ b/modules/form-record/export/multiple-controller.php @@ -27,9 +27,12 @@ public function do_export() { $this->form_id = $this->get_form_id(); // set fields without request - jet_fb_context()->set_parsers( - Block_Helper::get_blocks_by_post( $this->form_id ) + $blocks = apply_filters( + 'jet-form-builder/form-record/export/get-blocks-by-post', + Block_Helper::get_blocks_by_post( $this->form_id ), + $this->form_id ); + jet_fb_context()->set_parsers( $blocks ); $this->fields_columns = $this->get_field_columns(); $this->modify_extra_columns(); diff --git a/modules/validation/advanced-rules/server-side-rule.php b/modules/validation/advanced-rules/server-side-rule.php index c606e96ff..4e1c907a4 100644 --- a/modules/validation/advanced-rules/server-side-rule.php +++ b/modules/validation/advanced-rules/server-side-rule.php @@ -17,18 +17,118 @@ class Server_Side_Rule extends Rule { use Repository_Pattern_Trait; + /** + * Blacklist of dangerous functions that should never be allowed as callbacks. + * All values MUST be lowercase for case-insensitive comparison. + * + * @since 3.5.6.2 Security fix: expanded list and case-insensitive check + */ const NOT_ALLOWED = array( + // Debug/output functions 'var_dump', 'var_export', 'print_r', 'sprintf', 'printf', + // Command execution 'shell_exec', 'system', 'exec', + 'passthru', + 'proc_open', + 'popen', + 'pcntl_exec', + 'proc_nice', + 'proc_terminate', + 'proc_close', + // Code execution + 'eval', + 'assert', + 'create_function', + 'call_user_func', + 'call_user_func_array', + 'preg_replace_callback', + 'array_map', + 'array_filter', + 'array_reduce', + 'usort', + 'uasort', + 'uksort', + 'array_walk', + 'array_walk_recursive', + // File inclusion + 'include', + 'include_once', + 'require', + 'require_once', + // Serialization (object injection) 'unserialize', - 'file_get_contents', 'maybe_unserialize', + // File operations + 'file_get_contents', + 'file_put_contents', + 'fwrite', + 'fputs', + 'fopen', + 'readfile', + 'file', + 'fread', + 'fgets', + 'fgetc', + 'fgetcsv', + 'fpassthru', + 'move_uploaded_file', + 'copy', + 'rename', + 'unlink', + 'rmdir', + 'mkdir', + 'chmod', + 'chown', + 'chgrp', + // Network functions + 'curl_exec', + 'curl_multi_exec', + 'fsockopen', + 'pfsockopen', + 'stream_socket_client', + 'stream_socket_server', + // Dangerous PHP functions + 'parse_str', + 'extract', + 'putenv', + 'ini_set', + 'ini_alter', + 'dl', + 'mail', + 'header', + 'setcookie', + 'setrawcookie', + // POSIX functions + 'posix_kill', + 'posix_mkfifo', + 'posix_setpgid', + 'posix_setsid', + 'posix_setuid', + 'posix_setgid', + 'posix_seteuid', + 'posix_setegid', + // Apache functions + 'apache_child_terminate', + 'apache_setenv', + // Reflection/class manipulation + 'get_defined_functions', + 'get_defined_vars', + 'get_defined_constants', + 'phpinfo', + 'highlight_file', + 'show_source', + 'php_strip_whitespace', + 'get_cfg_var', + 'get_current_user', + 'getmyuid', + 'getmypid', + 'getenv', ); public function __construct() { @@ -94,13 +194,26 @@ protected function validate_custom( Field_Data_Parser $parser, string $function_ return (bool) call_user_func( $name, $parser->get_value(), $parser->get_context() ); } + /** + * Validate callback function name for security. + * + * @since 3.5.6.2 + * + * @param string $function_name The function name to validate. + * + * @return string Empty string if invalid, function name if valid. + */ protected function validate_callback( string $function_name ): string { $name = preg_replace( '/[^\w]/i', '', $function_name ); - if ( $name !== $function_name || - // not in the blacklist - in_array( $name, self::NOT_ALLOWED, true ) - ) { + if ( $name !== $function_name ) { + return ''; + } + + // Case-insensitive blacklist check (PHP function names are case-insensitive) + $name_lower = strtolower( $name ); + + if ( in_array( $name_lower, self::NOT_ALLOWED, true ) ) { return ''; } diff --git a/modules/validation/handlers/validation-handler.php b/modules/validation/handlers/validation-handler.php index 9d807b519..a2f9ba32d 100644 --- a/modules/validation/handlers/validation-handler.php +++ b/modules/validation/handlers/validation-handler.php @@ -2,18 +2,154 @@ namespace JFB_Modules\Validation\Handlers; use JFB_Modules\Validation\Rest_Api\Rest_Validation_Endpoint; -use JFB_Modules\Validation\Silence_Exception; +use Jet_Form_Builder\Exceptions\Repository_Exception; +use Jet_Form_Builder\Request\Exceptions\Plain_Value_Exception; use Jet_Form_Builder\Classes\Arrayable\Array_Tools; class Validation_Handler { + + /** + * Form post type constant for validation. + * + * @since 3.5.6.2 + */ + const FORM_POST_TYPE = 'jet-form-builder'; + + /** + * Validate that the given ID belongs to a published JetFormBuilder form. + * Supports revision IDs by resolving them to their parent form. + * + * @since 3.5.6.2 + * + * @param int $form_id The form ID to validate (can be a revision ID). + * + * @return bool True if valid, false otherwise. + */ + public static function validate_form_post_type( int $form_id ): bool { + if ( ! $form_id ) { + return false; + } + + $post = get_post( $form_id ); + + if ( ! $post ) { + return false; + } + + // Handle revisions: resolve to parent form and validate the parent + if ( 'revision' === $post->post_type ) { + $parent_id = wp_get_post_parent_id( $form_id ); + if ( ! $parent_id ) { + return false; + } + + $post = get_post( $parent_id ); + if ( ! $post ) { + return false; + } + } + + // Must be a JetFormBuilder form + if ( self::FORM_POST_TYPE !== $post->post_type ) { + return false; + } + + // Only allow published forms + if ( 'publish' !== $post->post_status ) { + return false; + } + + return true; + } + + /** + * Validate the signature from request body. + * + * @since 3.5.6.2 + * + * @param array $body Request body parameters. + * + * @return bool True if signature is valid, false otherwise. + */ + public static function validate_signature( array $body ): bool { + $form_id = absint( $body[ jet_fb_handler()->form_key ] ?? 0 ); + $field_path = $body[ Rest_Validation_Endpoint::FIELD_KEY ] ?? ''; + $rule_index = absint( $body[ Rest_Validation_Endpoint::RULE_INDEX_KEY ] ?? 0 ); + $signature = sanitize_text_field( $body[ Rest_Validation_Endpoint::SIGNATURE_KEY ] ?? '' ); + + if ( empty( $signature ) || empty( $form_id ) ) { + return false; + } + + // Normalize the field path for signature verification. + // Client sends full path like ['repeater_name', '0', 'field_name'] + // but signature is generated without row indexes: ['repeater_name', 'field_name'] + $normalized_path = self::normalize_field_path( $field_path ); + + $expected = Rest_Validation_Endpoint::generate_signature( $form_id, $normalized_path, $rule_index ); + + return hash_equals( $expected, $signature ); + } + + /** + * Normalize field path by removing numeric row indexes. + * Converts ['repeater', '0', 'field'] to ['repeater', 'field'] + * and sanitizes all path segments. + * + * @since 3.5.6.2 + * + * @param string|array $field_path The field path from request. + * + * @return string|array Normalized path without row indexes. + */ + protected static function normalize_field_path( $field_path ) { + if ( ! is_array( $field_path ) ) { + return sanitize_text_field( (string) $field_path ); + } + + $normalized = array(); + foreach ( $field_path as $segment ) { + $segment = sanitize_text_field( $segment ); + // Skip numeric segments (row indexes in repeaters) + if ( ! is_numeric( $segment ) ) { + $normalized[] = $segment; + } + } + + return $normalized; + } + public static function validate( $body ) { remove_all_actions( 'jet-form-builder/validate-field' ); + // Security: Validate form post type + $form_id = absint( $body[ jet_fb_handler()->form_key ] ?? 0 ); + + if ( ! self::validate_form_post_type( $form_id ) ) { + return array( + 'result' => false, + 'message' => __( 'Invalid form ID', 'jet-form-builder' ), + ); + } + + // Security: Validate signature + if ( ! self::validate_signature( $body ) ) { + return array( + 'result' => false, + 'message' => __( 'Invalid security signature', 'jet-form-builder' ), + ); + } + try { $request = new \WP_REST_Request(); $request->set_body_params( $body ); $parser = ( new Rest_Validation_Endpoint() )->get_parser_public( $request ); - } catch ( Silence_Exception $exception ) { + } catch ( Plain_Value_Exception $exception ) { + return array( + 'result' => false, + 'message' => __( 'Unresolved parser for field', 'jet-form-builder' ), + ); + } catch ( Repository_Exception $exception ) { return array( 'result' => false, 'message' => __( 'Unresolved parser for field', 'jet-form-builder' ), diff --git a/modules/validation/module.php b/modules/validation/module.php index df0a54ac7..26e81511a 100644 --- a/modules/validation/module.php +++ b/modules/validation/module.php @@ -19,6 +19,7 @@ use JFB_Components\Module\Base_Module_Url_Trait; use JFB_Modules\Block_Parsers\Field_Data_Parser; use JFB_Modules\Validation\Class_Validation_Handlers; +use JFB_Modules\Validation\Rest_Api\Rest_Validation_Endpoint; // If this file is called directly, abort. if ( ! defined( 'WPINC' ) ) { @@ -242,6 +243,30 @@ public function add_validation_block( Base $block ) { if ( ! empty( $rules ) ) { $this->get_rules()->prepare_rules( $rules ); + // Security: Add signatures for SSR validation rules + // For repeater fields, we include the repeater name in the signature + // but NOT the row index (which is dynamic). The signature binds the + // field to its structural path: [repeater_name, field_name] or just field_name + $form_id = jet_fb_live()->form_id; + $field_name = $block->block_attrs['name'] ?? ''; + $repeater_name = $block->get_repeater_name(); + + // Build canonical path for signature (without row index) + $signature_path = $repeater_name + ? array( $repeater_name, $field_name ) + : $field_name; + + foreach ( $rules as $index => &$rule ) { + if ( 'ssr' === ( $rule['type'] ?? '' ) ) { + $rule['_sig'] = Rest_Validation_Endpoint::generate_signature( + (int) $form_id, + $signature_path, + (int) $index + ); + } + } + unset( $rule ); + $block->add_attribute( 'data-validation-rules', Tools::encode_json( $rules ) diff --git a/modules/validation/rest-api/rest-validation-endpoint.php b/modules/validation/rest-api/rest-validation-endpoint.php index 14a12d4c4..7dde4e976 100644 --- a/modules/validation/rest-api/rest-validation-endpoint.php +++ b/modules/validation/rest-api/rest-validation-endpoint.php @@ -26,6 +26,7 @@ class Rest_Validation_Endpoint extends Rest_Api\Rest_Api_Endpoint_Base { const FIELD_KEY = '_jfb_validation_path'; const RULE_INDEX_KEY = '_jfb_validation_rule_index'; + const SIGNATURE_KEY = '_jfb_validation_sig'; public static function get_rest_base() { return 'validate-field'; @@ -35,15 +36,35 @@ public static function get_methods() { return \WP_REST_Server::CREATABLE; } + /** + * Generate a cryptographic signature for validation request. + * + * @since 3.5.6.2 + * + * @param int $form_id The form ID. + * @param string|array $field_path The field path. + * @param int $rule_index The rule index. + * + * @return string The generated signature. + */ + public static function generate_signature( int $form_id, $field_path, int $rule_index ): string { + $path_string = is_array( $field_path ) ? implode( '.', $field_path ) : (string) $field_path; + $data = $form_id . '|' . $path_string . '|' . $rule_index; + + return wp_hash( $data, 'nonce' ); + } + /** * @param \WP_REST_Request $request * * @return \WP_REST_Response - * @throws Repository_Exception */ public function run_callback( \WP_REST_Request $request ) { $body = $request->get_body_params(); + + // All security validation is centralized in Validation_Handler::validate() $result = Validation_Handler::validate( $body ); + return new WP_REST_Response( $result ); } diff --git a/readme.txt b/readme.txt index aaebf1174..53a4d86f5 100644 --- a/readme.txt +++ b/readme.txt @@ -4,7 +4,7 @@ Tags: blocks, forms, form builder, contact form, gutenberg, gutenberg forms, mul Requires at least: 6.1 Tested up to: 6.7.1 Requires PHP: 7.0 -Stable tag: 3.5.6.1 +Stable tag: 3.5.6.3 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -275,6 +275,14 @@ You can report security bugs through the Patchstack Vulnerability Disclosure Pro == Changelog == +**3.5.6.3** +- ADD: Support preview for AVIF files in Media Field +- ADD: `jet-form-builder/form-record/general-values-columns` filter for Form Record General Values Columns +- FIX: LFI vulnerability + +**3.5.6.2** +- FIX: RCE vulnerability + **3.5.6.1** - FIX: Compatibility with jetBooking 4.0.0 diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index fc44ca1c5..eda5b4057 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'crocoblock/jetformbuilder', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => 'df7eef93139074f35204bd3ed0fc03d3d263e22a', + 'reference' => '21e39eda416b2024c54d26fb7dc33550a16f8069', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -13,7 +13,7 @@ 'crocoblock/jetformbuilder' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => 'df7eef93139074f35204bd3ed0fc03d3d263e22a', + 'reference' => '21e39eda416b2024c54d26fb7dc33550a16f8069', 'type' => 'wordpress-plugin', 'install_path' => __DIR__ . '/../../', 'aliases' => array(),