diff --git a/template/builder.go b/template/builder.go
index 64530be62337de21dc5b93956df261ce5211a302..c606622667c7b17628f5a6f18bae86b0e49e19c3 100644
--- a/template/builder.go
+++ b/template/builder.go
@@ -3,7 +3,9 @@ package template
 import (
 	"bytes"
 	"context"
-	"text/template"
+	html "html/template"
+	"io"
+	text "text/template"
 
 	"git.perx.ru/perxis/perxis-go/pkg/collections"
 	"git.perx.ru/perxis/perxis-go/pkg/content"
@@ -11,32 +13,57 @@ import (
 	"git.perx.ru/perxis/perxis-go/pkg/spaces"
 )
 
+type Template interface {
+	Execute(w io.Writer, data any) error
+}
+
 type Builder struct {
 	ctx     context.Context
 	cnt     *content.Content
 	SpaceID string
 	EnvID   string
 	CollID  string
-	data    map[string]interface{}
+	data    map[string]any
 
 	// Для кеширования запросов
 	space       *spaces.Space
 	environment *environments.Environment
 	collection  *collections.Collection
+
+	// templateFunc парсит строку и возвращает шаблон для подстановки значений
+	templateFunc func(data string) (Template, error)
 }
 
-func NewBuilder(cnt *content.Content, space, env, col string) *Builder {
-	return &Builder{
+func NewBuilder(cnt *content.Content, space, env, col string) Builder {
+	b := Builder{
 		ctx:     context.Background(),
 		cnt:     cnt,
 		SpaceID: space,
 		EnvID:   env,
 		CollID:  col,
 	}
+	b.templateFunc = func(data string) (Template, error) {
+		return text.New("main_text").Funcs(b.getFuncs()).Parse(data)
+	}
+	return b
 }
 
-func (b *Builder) getFuncs() template.FuncMap {
-	return template.FuncMap{
+func NewHTMLBuilder(cnt *content.Content, space, env, col string) Builder {
+	b := Builder{
+		ctx:     context.Background(),
+		cnt:     cnt,
+		SpaceID: space,
+		EnvID:   env,
+		CollID:  col,
+	}
+	b.templateFunc = func(data string) (Template, error) {
+		return html.New("main_html").Funcs(b.getFuncs()).Parse(data)
+	}
+	return b
+}
+
+func (b *Builder) getFuncs() map[string]any {
+	return map[string]any{
 		"lookup": getLookup(b),
 		"system": getSystem(b),
 	}
@@ -84,14 +111,9 @@ func (b *Builder) Context() context.Context {
 	return b.ctx
 }
 
-func (b *Builder) Template() *template.Template {
-	return template.New("main").Funcs(b.getFuncs())
-}
-
 func (b *Builder) Execute(str string, data ...any) (string, error) {
-	t := b.Template()
 	buf := new(bytes.Buffer)
-	t, err := t.Parse(str)
+	t, err := b.templateFunc(str)
 	if err != nil {
 		return "", err
 	}
@@ -102,14 +124,13 @@ func (b *Builder) Execute(str string, data ...any) (string, error) {
 }
 
 func (b *Builder) ExecuteList(str []string, data ...any) ([]string, error) {
-	t := b.Template()
 	result := make([]string, len(str))
 	buffer := new(bytes.Buffer)
 	for i, tmpl := range str {
 		if tmpl == "" {
 			continue
 		}
-		t, err := t.Parse(tmpl)
+		t, err := b.templateFunc(tmpl)
 		if err != nil {
 			return []string{}, err
 		}
diff --git a/template/builder_test.go b/template/builder_test.go
index 128f706423f3ecc67097a76b8926fbed619eb1ce..d6cb0d575e50d5af0e33d04beaf046d8a2f120a7 100644
--- a/template/builder_test.go
+++ b/template/builder_test.go
@@ -19,16 +19,17 @@ import (
 
 func TestBuilder_Execute(t *testing.T) {
 	tests := []struct {
-		name    string
-		SpaceID string
-		EnvID   string
-		CollID  string
-		str     string
-		data    any
-		want    any
-		wantErr bool
+		name        string
+		SpaceID     string
+		EnvID       string
+		CollID      string
+		str         string
+		data        any
+		want        any
+		htmlBuilder bool
+		wantErr     bool
 
-		getCnt func() (cnt *content.Content, assertExpectations func(t *testing.T))
+		getCnt func(t *testing.T) *content.Content
 	}{
 		{name: "error", str: "hello {{ .a }}", data: "world", want: "", wantErr: true},
 		{name: "empty", str: "", data: "", want: "", wantErr: false},
@@ -36,100 +37,164 @@ func TestBuilder_Execute(t *testing.T) {
 		{name: "#2", str: "{{ . }}", data: "world", want: "world", wantErr: false},
 		{name: "#3 ", str: "", data: "world", want: "", wantErr: false},
 		{name: "#4 ", str: "hello", data: "world", want: "hello", wantErr: false},
-		{name: "lookup", SpaceID: "space", EnvID: "env", str: "hello, {{ lookup \"secrets.dev.key\" }}", data: "", want: "hello, Luk", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
-			itemsSvc := &mocksitems.Items{}
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data: map[string]interface{}{
-					"id":  "dev",
-					"key": "Luk",
-				},
-			}, nil).Once()
-			return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
-		}},
-		{name: "lookup with slice", SpaceID: "space", EnvID: "env", str: "numbers {{ lookup \"secrets.dev.slice\" }}", data: "", want: "numbers [1 2 3]", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
-			itemsSvc := &mocksitems.Items{}
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data: map[string]interface{}{
-					"id":    "dev",
-					"slice": []int{1, 2, 3},
-				},
-			}, nil).Once()
-			return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
-		}},
-		{name: "lookup with empty Data", SpaceID: "space", EnvID: "env", str: "numbers {{ lookup \"secrets.dev.slice\" }}", data: "", want: "numbers <no value>", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
-			itemsSvc := &mocksitems.Items{}
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data:         map[string]interface{}{},
-			}, nil).Once()
-			return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
-		}},
-		{name: "lookup with incorrect field", SpaceID: "space", EnvID: "env", str: "hello {{ lookup \"secrets.dev.incorrect\" }}", data: "", want: "hello <no value>", wantErr: false, getCnt: func() (*content.Content, func(t *testing.T)) {
-			itemsSvc := &mocksitems.Items{}
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
-				ID:           "dev",
-				SpaceID:      "space",
-				EnvID:        "env",
-				CollectionID: "secrets",
-				Data: map[string]interface{}{
-					"id":  "dev",
-					"key": "1234",
-				},
-			}, nil).Once()
-			return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
-		}},
-		{name: "lookup not found", SpaceID: "space", EnvID: "env", str: "hello {{ lookup \"secrets.prod.pass\" }}", data: "", want: "", wantErr: true, getCnt: func() (*content.Content, func(t *testing.T)) {
-			itemsSvc := &mocksitems.Items{}
-			itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "prod").Return(nil, errors.New("not found")).Once()
-			return &content.Content{Items: itemsSvc}, func(t *testing.T) { itemsSvc.AssertExpectations(t) }
-		}},
+		{
+			name:    "lookup",
+			SpaceID: "space",
+			EnvID:   "env",
+			str:     "hello, {{ lookup \"secrets.dev.key\" }}",
+			data:    "",
+			want:    "hello, Luk",
+			wantErr: false,
+			getCnt: func(t *testing.T) *content.Content {
+				itemsSvc := mocksitems.NewItems(t)
+				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
+					Return(&items.Item{
+						ID:           "dev",
+						SpaceID:      "space",
+						EnvID:        "env",
+						CollectionID: "secrets",
+						Data: map[string]interface{}{
+							"id":  "dev",
+							"key": "Luk",
+						},
+					}, nil).Once()
+				return &content.Content{Items: itemsSvc}
+			},
+		},
+		{
+			name:    "lookup with slice",
+			SpaceID: "space",
+			EnvID:   "env",
+			str:     "numbers {{ lookup \"secrets.dev.slice\" }}",
+			data:    "",
+			want:    "numbers [1 2 3]",
+			wantErr: false,
+			getCnt: func(t *testing.T) *content.Content {
+				itemsSvc := mocksitems.NewItems(t)
+				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
+					Return(&items.Item{
+						ID:           "dev",
+						SpaceID:      "space",
+						EnvID:        "env",
+						CollectionID: "secrets",
+						Data: map[string]interface{}{
+							"id":    "dev",
+							"slice": []int{1, 2, 3},
+						},
+					}, nil).Once()
+				return &content.Content{Items: itemsSvc}
+			},
+		},
+		{
+			name:    "lookup with empty Data",
+			SpaceID: "space",
+			EnvID:   "env",
+			str:     "numbers {{ lookup \"secrets.dev.slice\" }}",
+			data:    "",
+			want:    "numbers <no value>",
+			wantErr: false,
+			getCnt: func(t *testing.T) *content.Content {
+				itemsSvc := mocksitems.NewItems(t)
+				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
+					Return(&items.Item{
+						ID:           "dev",
+						SpaceID:      "space",
+						EnvID:        "env",
+						CollectionID: "secrets",
+						Data:         map[string]interface{}{},
+					}, nil).Once()
+				return &content.Content{Items: itemsSvc}
+			},
+		},
+		{
+			name:    "lookup with incorrect field",
+			SpaceID: "space",
+			EnvID:   "env",
+			str:     "hello {{ lookup \"secrets.dev.incorrect\" }}",
+			data:    "",
+			want:    "hello <no value>",
+			wantErr: false,
+			getCnt: func(t *testing.T) *content.Content {
+				itemsSvc := mocksitems.NewItems(t)
+				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
+					Return(&items.Item{
+						ID:           "dev",
+						SpaceID:      "space",
+						EnvID:        "env",
+						CollectionID: "secrets",
+						Data: map[string]interface{}{
+							"id":  "dev",
+							"key": "1234",
+						},
+					}, nil).Once()
+				return &content.Content{Items: itemsSvc}
+			},
+		},
+		{
+			name:    "lookup not found",
+			SpaceID: "space",
+			EnvID:   "env",
+			str:     "hello {{ lookup \"secrets.prod.pass\" }}",
+			data:    "",
+			want:    "",
+			wantErr: true,
+			getCnt: func(t *testing.T) *content.Content {
+				itemsSvc := mocksitems.NewItems(t)
+				itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "prod").
+					Return(nil, errors.New("not found")).Once()
+				return &content.Content{Items: itemsSvc}
+			},
+		},
 		{name: "lookup without itemID", SpaceID: "space", EnvID: "env", str: "hello {{ lookup \"secrets.pass\" }}", data: "", want: "", wantErr: true},
 		{name: "system#1", SpaceID: "space", str: "hello {{ system.SpaceID }}", data: "", want: "hello space", wantErr: false},
-		{name: "system space", SpaceID: "space", getCnt: func() (cnt *content.Content, assertExpectations func(t *testing.T)) {
-			spsSvc := &spsmocks.Spaces{}
+		{name: "system space", SpaceID: "space", getCnt: func(t *testing.T) *content.Content {
+			spsSvc := spsmocks.NewSpaces(t)
 			spsSvc.On("Get", context.Background(), "space").Return(&spaces.Space{Description: "description"}, nil).Once()
-			return &content.Content{Spaces: spsSvc}, func(t *testing.T) { spsSvc.AssertExpectations(t) }
+			return &content.Content{Spaces: spsSvc}
 		}, str: "{{ system.Space.Description }}", want: "description", wantErr: false},
-		{name: "system environment", SpaceID: "space", EnvID: "env", getCnt: func() (cnt *content.Content, assertExpectations func(t *testing.T)) {
-			envsSvc := &envsmocks.Environments{}
+		{name: "system environment", SpaceID: "space", EnvID: "env", getCnt: func(t *testing.T) *content.Content {
+			envsSvc := envsmocks.NewEnvironments(t)
 			envsSvc.On("Get", context.Background(), "space", "env").Return(&environments.Environment{Aliases: []string{"master"}}, nil).Once()
-			return &content.Content{Environments: envsSvc}, func(t *testing.T) { envsSvc.AssertExpectations(t) }
+			return &content.Content{Environments: envsSvc}
 		}, str: "{{ system.Environment.Aliases }}", want: "[master]", wantErr: false},
-		{name: "system collection", SpaceID: "space", EnvID: "env", CollID: "col", getCnt: func() (cnt *content.Content, assertExpectations func(t *testing.T)) {
-			collsSvc := &colsmocks.Collections{}
-			collsSvc.On("Get", context.Background(), "space", "env", "col").Return(&collections.Collection{Name: "cars"}, nil).Once()
-			return &content.Content{Collections: collsSvc}, func(t *testing.T) { collsSvc.AssertExpectations(t) }
-		}, str: "{{ system.Collection.Name }}", want: "cars", wantErr: false},
+		{
+			name:    "system collection",
+			SpaceID: "space",
+			EnvID:   "env",
+			CollID:  "col",
+			getCnt: func(t *testing.T) *content.Content {
+				collsSvc := colsmocks.NewCollections(t)
+				collsSvc.On("Get", context.Background(), "space", "env", "col").
+					Return(&collections.Collection{Name: "cars"}, nil).Once()
+				return &content.Content{Collections: collsSvc}
+			},
+			str:     "{{ system.Collection.Name }}",
+			want:    "cars",
+			wantErr: false,
+		},
 		{name: "system without account", SpaceID: "space", str: "hello {{ system.Organization.Name }}", want: "", wantErr: true},
+		{
+			name:        "with html builder",
+			str:         "{{ . }}",
+			data:        "<script>alert(localStorage.secret)</script>",
+			want:        "&lt;script&gt;alert(localStorage.secret)&lt;/script&gt;",
+			htmlBuilder: true,
+			wantErr:     false,
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
 			var cnt *content.Content
 			if tt.getCnt != nil {
-				var checkFn func(*testing.T)
-				cnt, checkFn = tt.getCnt()
-				defer checkFn(t)
+				cnt = tt.getCnt(t)
 			}
 
-			b := &Builder{
-				ctx:     context.Background(),
-				cnt:     cnt,
-				SpaceID: tt.SpaceID,
-				EnvID:   tt.EnvID,
-				CollID:  tt.CollID,
+			var b Builder
+			if tt.htmlBuilder {
+				b = NewHTMLBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
+			} else {
+				b = NewBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
 			}
-
 			got, err := b.Execute(tt.str, tt.data)
 			if tt.wantErr == true {
 				assert.Error(t, err)
@@ -187,16 +252,11 @@ func TestBuilder_ExecuteList(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			itemsSvc := &mocksitems.Items{}
+			itemsSvc := mocksitems.NewItems(t)
 			if tt.itemsCall != nil {
 				tt.itemsCall(itemsSvc)
 			}
-			b := &Builder{
-				ctx:     context.Background(),
-				cnt:     &content.Content{Items: itemsSvc},
-				SpaceID: tt.SpaceID,
-				EnvID:   tt.EnvID,
-			}
+			b := NewBuilder(&content.Content{Items: itemsSvc}, tt.SpaceID, tt.EnvID, "")
 
 			got, err := b.ExecuteList(tt.str, tt.data)
 			if tt.wantErr == true {
@@ -205,7 +265,6 @@ func TestBuilder_ExecuteList(t *testing.T) {
 				assert.NoError(t, err)
 			}
 			assert.Equal(t, tt.want, got)
-			itemsSvc.AssertExpectations(t)
 		})
 	}
 }
@@ -256,16 +315,11 @@ func TestBuilder_ExecuteMap(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			itemsSvc := &mocksitems.Items{}
+			itemsSvc := mocksitems.NewItems(t)
 			if tt.itemsCall != nil {
 				tt.itemsCall(itemsSvc)
 			}
-			b := &Builder{
-				ctx:     context.Background(),
-				cnt:     &content.Content{Items: itemsSvc},
-				SpaceID: tt.SpaceID,
-				EnvID:   tt.EnvID,
-			}
+			b := NewBuilder(&content.Content{Items: itemsSvc}, tt.SpaceID, tt.EnvID, "")
 
 			got, err := b.ExecuteMap(tt.str, tt.data)
 			if tt.wantErr == true {
@@ -274,7 +328,6 @@ func TestBuilder_ExecuteMap(t *testing.T) {
 				assert.NoError(t, err)
 			}
 			assert.Equal(t, tt.want, got)
-			itemsSvc.AssertExpectations(t)
 		})
 	}
 }