From 3082e558adca612fbbd60a430294fca03e1e2143 Mon Sep 17 00:00:00 2001 From: SEWADE <31615609+SEWADE@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:03:09 +0200 Subject: [PATCH 1/3] fix: add ExtraData to CustomField struct for select options --- paperless.go | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/paperless.go b/paperless.go index 170658db..e4d716ee 100644 --- a/paperless.go +++ b/paperless.go @@ -39,10 +39,20 @@ type PaperlessClient struct { } // CustomField represents a custom field from the Paperless-ngx API +type SelectOption struct { + ID string `json:"id"` + Label string `json:"label"` +} + +type CustomFieldExtraData struct { + SelectOptions []SelectOption `json:"select_options"` +} + type CustomField struct { - ID int `json:"id"` - Name string `json:"name"` - DataType string `json:"data_type"` + ID int `json:"id"` + Name string `json:"name"` + DataType string `json:"data_type"` + ExtraData *CustomFieldExtraData `json:"extra_data"` } // DocumentType represents a document type from the Paperless-ngx API From f24c3d1dffe6a04d8b0380a4ca343e61c613a95c Mon Sep 17 00:00:00 2001 From: SEWADE <31615609+SEWADE@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:19:43 +0200 Subject: [PATCH 2/3] fix: include select options in LLM prompt for custom fields --- app_llm.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app_llm.go b/app_llm.go index 98bd5d93..21c11558 100644 --- a/app_llm.go +++ b/app_llm.go @@ -401,7 +401,15 @@ func (app *App) getSuggestedCustomFields(ctx context.Context, doc Document, sele var xmlBuilder strings.Builder xmlBuilder.WriteString("\n") for _, field := range selectedCustomFields { - xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType)) + if field.DataType == "select" && field.ExtraData != nil && len(field.ExtraData.SelectOptions) > 0 { + xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType)) + for _, opt := range field.ExtraData.SelectOptions { + xmlBuilder.WriteString(fmt.Sprintf(" \n", opt.ID, opt.Label)) + } + xmlBuilder.WriteString(" \n") + } else { + xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType)) + } } xmlBuilder.WriteString("") customFieldsXML := xmlBuilder.String() From c8b434f48cc61b4a9f9fc70887f2d500dc86c3ad Mon Sep 17 00:00:00 2001 From: SEWADE <31615609+SEWADE@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:23:51 +0200 Subject: [PATCH 3/3] fix: escape XML attributes and text in custom field prompt --- app_llm.go | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/app_llm.go b/app_llm.go index 21c11558..422526ad 100644 --- a/app_llm.go +++ b/app_llm.go @@ -373,7 +373,22 @@ func (app *App) getSuggestedCreatedDate(ctx context.Context, content string, log result := stripReasoning(completion.Choices[0].Content) return strings.TrimSpace(strings.Trim(result, "\"")), nil } +var xmlAttrEscaper = strings.NewReplacer( + "&", "&", + `"`, """, + "'", "'", + "<", "<", + ">", ">", +) + +var xmlTextEscaper = strings.NewReplacer( + "&", "&", + "<", "<", + ">", ">", +) +func escapeXMLAttr(s string) string { return xmlAttrEscaper.Replace(s) } +func escapeXMLText(s string) string { return xmlTextEscaper.Replace(s) } // getSuggestedCustomFields generates suggested custom fields for a document using the LLM func (app *App) getSuggestedCustomFields(ctx context.Context, doc Document, selectedFieldIDs []int, logger *logrus.Entry) ([]CustomFieldSuggestion, error) { // Fetch all available custom fields @@ -402,13 +417,13 @@ func (app *App) getSuggestedCustomFields(ctx context.Context, doc Document, sele xmlBuilder.WriteString("\n") for _, field := range selectedCustomFields { if field.DataType == "select" && field.ExtraData != nil && len(field.ExtraData.SelectOptions) > 0 { - xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType)) + xmlBuilder.WriteString(fmt.Sprintf(" \n", escapeXMLAttr(field.Name), escapeXMLAttr(field.DataType))) for _, opt := range field.ExtraData.SelectOptions { - xmlBuilder.WriteString(fmt.Sprintf(" \n", opt.ID, opt.Label)) + xmlBuilder.WriteString(fmt.Sprintf(" \n", escapeXMLAttr(opt.ID), escapeXMLText(opt.Label))) } xmlBuilder.WriteString(" \n") } else { - xmlBuilder.WriteString(fmt.Sprintf(" \n", field.Name, field.DataType)) + xmlBuilder.WriteString(fmt.Sprintf(" \n", escapeXMLAttr(field.Name), escapeXMLAttr(field.DataType))) } } xmlBuilder.WriteString("")