diff --git a/client/web/compose/src/components/ModuleFields/Configurator/String.vue b/client/web/compose/src/components/ModuleFields/Configurator/String.vue
index bd266c950d..3b145df251 100644
--- a/client/web/compose/src/components/ModuleFields/Configurator/String.vue
+++ b/client/web/compose/src/components/ModuleFields/Configurator/String.vue
@@ -7,6 +7,25 @@
{{ $t('kind.string.richText') }}
+
+
+
+ {{ $t('kind.string.sanitizeXSS.label') }}
+
+
+
+
+
@@ -19,5 +38,14 @@ export default {
},
extends: base,
+
+ data () {
+ return {
+ checkboxLabel: {
+ on: this.$t('general:label.yes'),
+ off: this.$t('general:label.no'),
+ },
+ }
+ },
}
diff --git a/client/web/compose/src/components/ModuleFields/Viewer/String.vue b/client/web/compose/src/components/ModuleFields/Viewer/String.vue
index 28c3a1f034..597985c36a 100644
--- a/client/web/compose/src/components/ModuleFields/Viewer/String.vue
+++ b/client/web/compose/src/components/ModuleFields/Viewer/String.vue
@@ -5,10 +5,19 @@
class="rt-content"
>
+
+
+ {{ formatted }}
+
diff --git a/client/web/compose/src/components/PageBlocks/RecordListBase.vue b/client/web/compose/src/components/PageBlocks/RecordListBase.vue
index 320c014e57..81fe4def1c 100644
--- a/client/web/compose/src/components/PageBlocks/RecordListBase.vue
+++ b/client/web/compose/src/components/PageBlocks/RecordListBase.vue
@@ -2622,6 +2622,14 @@ tr:hover .inline-actions {
width: 1%;
font-family: var(--font-regular) !important;
}
+
+ tbody {
+ tr {
+ td:nth-last-child(2) {
+ padding-right: 5rem;
+ }
+ }
+ }
}
.record-list-footer {
diff --git a/client/web/compose/src/components/PageBlocks/RecordListConfigurator.vue b/client/web/compose/src/components/PageBlocks/RecordListConfigurator.vue
index a63c831ff4..4ee0378555 100644
--- a/client/web/compose/src/components/PageBlocks/RecordListConfigurator.vue
+++ b/client/web/compose/src/components/PageBlocks/RecordListConfigurator.vue
@@ -791,21 +791,21 @@
cols="12"
lg="6"
>
-
+
{{ $t('recordList.inlineEdit.enabled') }}
@@ -112,9 +112,9 @@
@@ -372,4 +372,8 @@ export default {
.item:hover {
background-color: var(--gray-200);
}
+
+.download-button:hover {
+ color: var(--primary) !important;
+}
diff --git a/extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page_layout.go b/extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page_layout.go
index 79cc70a136..7592ac25d3 100644
--- a/extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page_layout.go
+++ b/extra/server-discovery/vendor/github.com/cortezaproject/corteza/server/compose/types/page_layout.go
@@ -175,7 +175,7 @@ func (p *PageLayout) decodeTranslations(tt locale.ResourceTranslationIndex) {
"{{actionID}}", strconv.FormatUint(actionID, 10),
)
- if aux = tt.FindByKey(rpl.Replace(LocaleKeyPagePageBlockBlockIDTitle.Path)); aux != nil {
+ if aux = tt.FindByKey(rpl.Replace(LocaleKeyPageLayoutConfigActionsActionIDMetaLabel.Path)); aux != nil {
p.Config.Actions[i].Meta.Label = aux.Msg
}
}
diff --git a/lib/js/src/compose/types/module-field/string.ts b/lib/js/src/compose/types/module-field/string.ts
index f18a941600..58292310aa 100644
--- a/lib/js/src/compose/types/module-field/string.ts
+++ b/lib/js/src/compose/types/module-field/string.ts
@@ -7,6 +7,7 @@ interface StringOptions extends Options {
multiLine: boolean;
useRichTextEditor: boolean;
multiDelimiter: string;
+ sanitizeXSS: boolean;
}
const defaults = (): Readonly => Object.freeze({
@@ -14,6 +15,7 @@ const defaults = (): Readonly => Object.freeze({
multiLine: false,
useRichTextEditor: false,
multiDelimiter: '\n',
+ sanitizeXSS: true,
})
export class ModuleFieldString extends ModuleField {
@@ -32,7 +34,7 @@ export class ModuleFieldString extends ModuleField {
super.applyOptions(o)
Apply(this.options, o, String, 'multiDelimiter')
- Apply(this.options, o, Boolean, 'multiLine', 'useRichTextEditor')
+ Apply(this.options, o, Boolean, 'multiLine', 'useRichTextEditor', 'sanitizeXSS')
}
}
diff --git a/locale/en/corteza-webapp-compose/field.yaml b/locale/en/corteza-webapp-compose/field.yaml
index a9e93c5ca7..7026e10571 100644
--- a/locale/en/corteza-webapp-compose/field.yaml
+++ b/locale/en/corteza-webapp-compose/field.yaml
@@ -164,6 +164,9 @@ kind:
label: String
multiLine: Multi line
richText: Use Rich Text Editor
+ sanitizeXSS:
+ label: XSS Sanitization
+ warning: Disabling XSS sanitization is dangerous and may expose your application to security vulnerabilities. Only disable if you fully trust the data source and understand the risks.
url:
example: 'Example URL: https://example.com'
label: URL
diff --git a/server/automation/automation/loop_handler.go b/server/automation/automation/loop_handler.go
index 86c6c3dbd4..654c25cc6e 100644
--- a/server/automation/automation/loop_handler.go
+++ b/server/automation/automation/loop_handler.go
@@ -26,6 +26,15 @@ func LoopHandler(reg loopHandlerRegistry, p expr.Parsable) *loopHandler {
return h
}
+func signOf(n int64) int64 {
+ if n > 0 {
+ return 1
+ } else if n < 0 {
+ return -1
+ }
+ return 0
+}
+
func (h loopHandler) sequence(_ context.Context, args *loopSequenceArgs) (wfexec.IteratorHandler, error) {
if !args.hasFirst {
args.First = 0
@@ -39,8 +48,9 @@ func (h loopHandler) sequence(_ context.Context, args *loopSequenceArgs) (wfexec
args.Step = 1
}
- if args.First*(args.Step/args.Step) >= args.Last*(args.Step/args.Step) {
- return nil, fmt.Errorf("failed to initialize counter iterator with first step greater than last")
+ sign := signOf(args.Step)
+ if args.First*sign >= args.Last*sign {
+ return nil, fmt.Errorf("failed to initialize counter iterator with first value greater than last or with zero step")
}
i := &sequenceIterator{
diff --git a/server/compose/service/values/sanitizer.go b/server/compose/service/values/sanitizer.go
index 30a92d44cd..0689e21456 100644
--- a/server/compose/service/values/sanitizer.go
+++ b/server/compose/service/values/sanitizer.go
@@ -140,7 +140,7 @@ func (s sanitizer) RunXSS(m *types.Module, vv types.RecordValueSet) types.Record
switch strings.ToLower(f.Kind) {
case "string":
- v.Value = sString(v.Value)
+ v.Value = sString(v.Value, f.Options.SanitizeXSS())
}
}
@@ -258,13 +258,17 @@ func sNumber(num interface{}, p uint, s uint) string {
return str
}
-func sString(str interface{}) string {
+func sString(str interface{}, sanitizeXSS bool) string {
base, err := cast.ToStringE(str)
if err != nil {
return ""
}
- return xss.RichText(base)
+ if sanitizeXSS {
+ return xss.RichText(base)
+ }
+
+ return base
}
// sanitize casts value to field kind format
@@ -278,7 +282,7 @@ func sanitize(f *types.ModuleField, v interface{}) string {
// @todo !! This is temporary; precision and scale require a rework
v = sNumber(v, maxPrecision, f.Options.Precision())
case "string":
- v = sString(v)
+ v = sString(v, f.Options.SanitizeXSS())
}
return fmt.Sprintf("%v", v)
diff --git a/server/compose/types/module_field_options.go b/server/compose/types/module_field_options.go
index c1ee1d532c..6ba8810706 100644
--- a/server/compose/types/module_field_options.go
+++ b/server/compose/types/module_field_options.go
@@ -4,9 +4,10 @@ import (
"database/sql/driver"
"encoding/json"
"fmt"
+ "strconv"
+
"github.com/cortezaproject/corteza/server/pkg/sql"
"github.com/spf13/cast"
- "strconv"
)
type (
@@ -37,6 +38,8 @@ const (
moduleFieldNumberOptionPrecision = "precision"
moduleFieldNumberOptionPrecisionMin uint = 0
moduleFieldNumberOptionPrecisionMax uint = 6
+
+ moduleFieldStringOptionSanitizeXSS = "sanitizeXSS"
)
func (opt *ModuleFieldOptions) Scan(src any) error { return sql.ParseJSON(src, opt) }
@@ -181,3 +184,17 @@ func (opt ModuleFieldOptions) Index() *ModuleFieldOptionIndex {
return nil
}
+
+// SanitizeXSS - should XSS sanitization be applied to this string field?
+func (opt ModuleFieldOptions) SanitizeXSS() bool {
+ // Default to true if not set
+ if _, has := opt[moduleFieldStringOptionSanitizeXSS]; !has {
+ return true
+ }
+ return opt.Bool(moduleFieldStringOptionSanitizeXSS)
+}
+
+// SetSanitizeXSS - set whether XSS sanitization should be applied to this string field
+func (opt ModuleFieldOptions) SetSanitizeXSS(value bool) {
+ opt[moduleFieldStringOptionSanitizeXSS] = value
+}