Skip to content

Input protection decisions

This inventory records shared protections, dedicated boundaries and justified exemptions. Inputs with the same decision are grouped by surface; it is not a manually maintained copy of every route or OpenAPI field. The input-security guide defines the profiles, budgets and runtime errors. Review procedure belongs to aihub-input-safety-review.

SafetyChecked enforces runtime budgets without adding maxLength to OpenAPI. An absent schema length constraint does not establish a vulnerability, and a text check does not establish resistance to injection. Routes, aliases and parameter contracts remain defined by source routers and mounted OpenAPI.

Shared protections by surface

Surface and inputs Decision and consumer boundary Source owner
Talent Marketplace prefill: client_id, query, supplied filter descriptors and optional locale Closed schemas and SafetyChecked, 4,000 query characters and 100 unique descriptors; authenticated tenant binding, fixed SQL queries with bound parameters, and Smart Editor ownership/BO checks. Only server-loaded choices reach a bounded user-role context under the shared policy. No safety classifier or tools: first-stage filter identities must belong to the supplied definitions before any value reads; second-stage selections are validated against the selected catalogue, type and cardinality. The calling backend owns end-user permission checks and the supplied standard-filter list. src/talent_intelligence/api/marketplace_filters_router.py, schemas/marketplace_filters.py, repositories/marketplace_filters.py, services/marketplace_filters.py
Galaxy path/rationale JSON bodies and stored job/skill context Closed request schemas and SafetyChecked budgets; body tenant must match authenticated tenant context. MongoDB tenant databases and PostgreSQL bound tenant/ID predicates isolate reads. Prompts use a bounded user-role context under the shared policy; generated IDs and transitions are checked against authoritative jobs. No classifier or tool execution is involved. src/matching/job_proximity/api/galaxy_router.py, src/matching/job_proximity/galaxy
POST /api/v1/skills-library/skills_single and /skills-library/skills_single: search_query, other request data SkillSearchText for the query, shared budgets for the surrounding request. Query values remain data; the parsed input is logged as compact JSON at INFO through the shared log-redaction filter. src/skills_library/api/search_router.py
/chatbot: chat messages/nested data, history, preferences, feedback, suggestions and topics Domain/shared checks at typed or manual boundaries; transcriptions are revalidated. Chat mode is determined by the server through the main graph. Oria gates retrieval, preference extraction and agents; direct preference writes use deterministic validation. Stored context remains user-role data. src/chatbot/chat, history, long_term_memory, event, agentic
Generic interaction replies on HTTP chat Closed typed action/identifier contract, shared chat text budgets, owner/tenant/checkpoint checks and atomic admission. User answers rerun the existing safety gate before resuming; Question IDs must cover the checkpoint exactly; all nested answer texts are validated and checked together with canonical questions. Per-question skip and global cancel are app-owned actions without user text. Selections accept opaque question-local option IDs, validated for membership, uniqueness and single/multiple cardinality before reservation or safety calls. Labels and business targets come only from the checkpoint. Text and option IDs are mutually exclusive; alternative text requires explicit permission on the question. Canonical selected labels reach the safety gate and history; the frontend cannot supply labels or target IDs. No uploads on resume. src/chatbot/chat/interactions.py, schemas/inputs.py
Simulation HTTP/WebSocket context and /mcp agent-tool arguments Shared budgets and separate user context. Live uses a closed audio/control event schema; context.voice must be a string in the SDK voice catalogue or one of the documented French voices, brise and sillage. MCP tool access is authenticated. Neither inherits the Oria classifier automatically. src/chatbot/simulation, src/chatbot/voice/configuration.py, src/common/security/live_inputs.py, src/mcp_server
Talent Intelligence, matching and PwC: descriptions, similarity texts, objectives, prompt forms, rationale and feedback Shared budgets; external LLM context stays in user messages. Bound query values, sort allowlists and fixed-key/numeric filters remain separate controls. Prompt-file rows and grouped generation context are checked too. Prompt-engine provider selection uses an allowlist and model_profile a closed enum; model names and endpoint URLs come only from server configuration. OVH uses a fixed shared API origin and accepts model IDs instead of endpoint URLs. src/talent_intelligence, src/matching, src/pwc_jom_compatibility
On-demand training rationale: optional search_query SafetyChecked query text, maximum 4,000 characters, passed as JSON user-message data under the shared untrusted-context policy. User/training IDs use existing database lookups; search text is never interpolated into a database query. Query-mode learning evidence allows only documented content fields, excluding identifiers and vectors. No tool execution or persistence. src/matching/training/api/router.py, src/matching/training/rationale.py
Lexi: job/skill bodies, nested extras, lists and generated assignments Shared budgets; client content remains user data under the common context policy. Output mappings constrain assignments to supplied IDs. Duplicate decisions keep labels/descriptions in untrusted evidence, use application-owned pair IDs and closed categories, and validate complete answers before grouping. The duplicate object request rejects unknown fields. Downloaded catalogues have a dedicated budget and destination boundary below. src/lexi/api/router.py, src/lexi/services
GenAI Chat Completions and model discovery: selectors, generation controls, schemas, messages, tools and inline documents Closed request and control schemas, bounded messages/text/JSON Schema/cache keys, catalogue-only model/provider selection (OpenAI, Azure OpenAI, Anthropic and OVH) and shared discovery/capability validation. Model overrides profile; request options are rejected when unsupported, including forced tool choices for Sonnet 5.5 and Opus 5.5 and incompatible Chat Completions tool/reasoning combinations for GPT-5.4 and later, and legacy cache-retention fields on GPT-5.6/GPT-6. Discovery projects only public model metadata and never returns credentials. Validation errors use sanitized OpenAI-shaped envelopes without echoing payloads. Inline PDF/DOCX parts have a 20 MiB decoded-byte ceiling, PDF page and DOCX image count ceilings, plus DOCX archive expansion limits. Documents are treated as untrusted user content; no uploaded content or filename is logged. The endpoint does not execute caller-supplied tools. Native anthropic_content replay blocks remain subject to the message metadata budget and are forwarded only as assistant content. Direct document blocks in this extension are rejected before configuration lookup or generation; nested tool-result documents and signed blocks remain unchanged. Provider signature validation remains authoritative. src/genai/api/router.py, src/genai/services
Data Exchange, entities and queue consumers: open bodies, updates, imported records and stored prompt data Shared checks at HTTP boundaries and per record before persistence, standardization or LLM/embedding work. Existing retry/continuation ownership is preserved. Open bodies and optional empty-body defaults retain their contracts. src/data_exchange, src/entity, src/common/connectors/consumers, src/common/infer_skills
Administration and tenant routing: query/path/list inputs, Client-ID Shared budgets complement administrator authentication and resource-specific parsers. They do not grant access to a tenant, database or collection. src/admin/api, src/auth/security.py
Client configuration normalization: client_id, data_source and stored settings Administrator authentication and shared query budgets protect selection. The template path is server-owned. Stored privileged configuration uses structural normalization, credential sanitization and SQL import identifier validation rather than generic text filtering. Dynamic filters, rules and other documented client mappings are preserved. An unknown explicit client fails before global enumeration. src/admin/api/configuration_router.py, src/admin/services/configuration_normalization.py, src/admin/services/configuration.py
Administrative operations: client resource preparation, background imports, indexes with apply=true, maintenance, matching, reports and migrations Administrator authentication and input budgets run before admission. Positive worker counts remain validated in the HTTP query. Accepted operations retain validated values and configured recipients; exceptions are excluded from notification payloads. Index dry runs (apply=false) and opportunity checks (check_only=true) await the read-only report directly without task admission or email configuration. Opportunity synchronization checks upload extension/size and retains bounded bytes, never the request-owned upload handle; identifier validation also runs in both modes. Only check_only=false starts a background synchronization task. Migration targets come only from server settings; recipient lookup uses the template before database initialization. Client creation retains query validation and recipient preflight before resource writes, awaits reconciliation before HTTP 200, and schedules only initial imports. Resource errors return sanitized HTTP 500; capacity/shutdown rejection remains 503. initialisation_import defaults to true. Client /batch-processing/client/imports, /matchings and /full POST routes retain shared query validation, authenticated environment resolution and pre-admission recipient validation; imports and full processing require MongoDB. Matching worker counts must be positive; direct imports use a fixed eight-worker bound. Queued initialization requires SmartMobility, bound numeric SQL IDs and a configured tenant queue prefix. Message flags are strict booleans; initialization messages cannot request matching or job deletion. src/admin/api, src/admin/services/background_operations.py, src/admin/services/postgres_migration_service.py
Administrative operation status: GET /admin/api/v1/operations/{task_id} Administrator authentication and a typed UUID path parameter; general text validation is inapplicable to this identifier. Only the canonical UUID reaches the read-only local worker protocol. Private owner-only socket directory, bounded messages/connections, and lookup timeouts protect that boundary. Responses expose only the UUID and state, never operation payloads or exception text. No database or durable history. src/admin/api/operations_router.py, src/admin/services/operation_status.py
Administrative skill embedding regeneration: stored source and English titles Shared text budgets are rechecked before language detection, translation or embedding. Translation uses the existing data-only user message and grouped provider chain; unsuccessful source-text fallback is never persisted as title_en. Collection access remains administrator-only and vector paths come from the configured model. src/admin/api/skills_library_router.py, src/common/utils/generate_embeddings.py, translation_tools.py
PUT /chatbot/api/v1/configs: text fields and Base64 images Text budgets plus dedicated image byte checks. The supplied user_id is checked for administrator status; the identity-binding gap remains open below. src/chatbot/config/router.py

Opportunity synchronization validates every uploaded identifier before comparison or writes. Nested identifier arrays and objects are rejected with the existing sanitized validation error; valid identifier types are preserved. Fixed MongoDB operators receive IDs only as $in values, in batches of at most 500. The upload size limit and administrator authentication continue to protect this manual file parser in src/entity/opportunity/maintenance.py.

Shared helper behavior and HTTP parameter handling are covered in tests/common/security; endpoint, manual parser, upload and graph tests live under the corresponding owning test packages. Schema equality alone cannot prove a runtime validator is active.

POST /api/v1/feedback/process applies shared budgets to its open JSON body and client/data-source query strings. Its boolean call_from_consumer query flag controls raw Datalake archival and uses FastAPI boolean parsing. These controls are owned by src/matching/job_offer/api/feedback_router.py.

Dedicated boundaries and exemptions

GET /admin/api/v1/clients/exists requires administrator authentication and SafetyChecked query inputs. client_id must be nonblank. Optional blank/null inputs resolve to deployment-owned defaults; a nonblank environment must match application state. The read-only MongoDB filter uses literal identity values under fixed field names and projects only _id. It never accepts operators or database names from the caller. Owners: src/admin/api/client_router.py and src/admin/services/client_lookup.py.

POST /admin/api/v1/clients keeps administrator authentication and shared SafetyChecked validation for the import database/schema query value, including when an explicit override replaces the SM_DATABASE deployment default. The optional environment is constrained to the application's resolved environment by get_admin_api_environment; it cannot redirect provisioning to another deployment. Database defaults are deployment-owned configuration, with smartmobility_io used only when SM_DATABASE is unset or blank. Owners: src/admin/api/client_router.py and src/auth/security.py. RabbitMQ prefix inputs retain shared text validation; omitted, empty and whitespace-only values use the deployment-owned prefix generated in src/admin/services/client_provisioning.py. Nonblank overrides are preserved.

POST /api/v1/jpm/rationale uses SafetyChecked on its closed request model, bounded identifiers and a required locale. The route rejects a client that differs from the authenticated context before reading data. MongoDB reads use the resolved tenant database; PostgreSQL employee and job reads explicitly bind the client ID. Only scoring skills and job text are sent as untrusted user-role context, limited to 120,000 characters, alongside the shared system context policy. The model has no tools or writes. Prompts and generated rationales are excluded from operational events. Owners: src/matching/job_proximity/api/rationale_router.py and src/matching/job_proximity/rationale/.

POST /api/v1/performance-goals/suggestions and POST /api/v1/performance-goals/key-results/suggestions use SafetyChecked on closed request models with bounded identifiers, a required locale, enumerated statuses and measure types, ISO 4217 currency codes, finite values and bounded context lists. The route rejects a client that differs from the authenticated context before reading data. The MongoDB employee read binds the resolved tenant database and an equality userId filter; the PostgreSQL read binds the client ID. Goal, key result and profile text is sent only as untrusted user-role JSON, limited to 120,000 characters, alongside the shared system context policy. The model has no tools or writes, and generated output is revalidated against the response contracts. Prompts and suggestions are excluded from operational events. Owners: src/talent_intelligence/api/performance_goals_router.py and src/talent_intelligence/services/performance_goals.py.

Input or consumer Why generic text validation is insufficient or inapplicable Existing protection and source
API keys, JWTs and authentication headers Credentials must retain their exact representation. Authentication parsers in src/auth/security.py; Client-ID is checked separately.
Consumer-format user import: achiever reasons, experience titles/descriptions, previous company names and educational institute names Existing imported text can contain DEL/C1 characters (U+007F–U+009F); rejecting them prevents otherwise valid imports. src/entity/user/input_validation.py limits this tolerance to education[].educationalInstitute.instituteName, achievers[].reason, previousExperiences[].companyName/jobTitle/experienceDescription, clientExperiences[].experienceDescription, and standardized experiences[].experienceTitle/experienceDescription/description. HTTP import, user flow and standardization preserve the incoming values and dedicated import budgets. get_prompts_for_user also accepts titles as root keys in the internal inference mapping and retains titles/descriptions in user-role messages; nested metadata keys and dates remain strict. Company names are ordinary text; the existing PwC transformation does not copy previousExperiences[].companyName to standardized experiences. Other company-name fields, received JSON keys, identifiers and C0 controls stay restricted. The node budget is 250,000 per record; text and depth limits are unchanged.
Consumer-format opportunity import: descriptions and skill definitions Imported narrative text needs the same DEL/C1 tolerance as user imports. src/entity/opportunity/input_validation.py permits DEL/C1 only in jobOpportunityDescription, standardized description, skills[].professionalSkillDefinition and the description passed to opportunity skill/title inference. The HTTP adapter, flow and standardization reuse this profile; titles, IDs, keys and other input budgets remain strict. Job/training inference retains the default profile. The node budget is 250,000 per record; text and depth limits are unchanged.
Consumer-format feedback import: feedbackText, declineReason These optional narrative values can contain legacy DEL/C1 text in the open import body. src/matching/job_offer/feedback/input_validation.py is used at HTTP and flow boundaries. IDs, flags, keys, other controls and input budgets remain strict. Existing PwC transformation and score behavior are preserved; optional text remains ignored by that transformation. The node budget is 250,000 per record; text and depth limits are unchanged.
Privileged tenant configuration, including secrets, system prompts and SQL configuration These are administrator capabilities, not ordinary free text; recursive validation would mix credentials/binary content with executable configuration. Administrator authentication, validate_sql_import_config, numeric SQL-ID parsing (including user/offer technical_classification Smart Editor IDs), quoted identifiers, bound values and literal PyMongo collection selection in src/admin and shared connectors.
Provider URLs and Lexi catalogue/callback destinations Length or URL syntax alone does not prevent SSRF or cross-tenant access. src/common/connectors/ai_providers/providers/ovh_provider.py fixes the OVH API origin; Lexi provided_catalog.py and hrx_endpoints.py enforce configured HRX origin, exact organization/referential/run path, signed token, no redirects, digest and scope. Callbacks use deployment-owned origin and fixed paths.
Knowledge Base configured public URLs, page links and redirects Dedicated destination controls: exact host, allowed path subtrees, standard ports and connection-time public DNS validation. HTTP may upgrade to HTTPS; same-host HTTP destinations are kept on HTTPS once established. Correction never bypasses credentials, query, path or destination validation, changes hosts, or retries over HTTP. src/chatbot/agentic/core/knowledge_web.py and knowledge_navigation.py; model tools accept only discovered page handles and listed sections, with shared request-local page/read/depth limits; bounded requests and content, no cookies/proxies or user context forwarded to sites. Diagnostics use stable codes, error types and HTTP statuses without URLs or raw errors.
CV parsing ai_provider query parameter A catalogue selector is not prompt text, a URL or a database operator. src/talent_intelligence/api/resume_parsing_router.py uses the shared configuration loader with an empty tenant configuration. configuration_resolver.py selects an exact configured provider key; omitted, empty and unknown values all select the database default, then the built-in default if none exists. Database failures propagate. Callers cannot supply a model, credential or destination. Generic text scrubbing is inapplicable to this lookup-only selector.
File bytes and Base64 contents Binary formats need byte/expansion limits and parsers, not text scrubbing. Upload readers, image decoding and src/common/security/upload_archives.py; consumed spreadsheet IDs and prompt rows receive text checks after parsing. POST /api/v1/talent-intelligence/cv/parse allow-lists formats per provider and applies shared upload/archive limits plus a hard 10 MiB limit. OpenAI receives original inline files after signature and safe office-XML validation; these checks do not fully validate PDF/legacy DOC internals. Other providers use bounded local extraction (2 active + 2 queued per worker, 30 seconds, page/pixel/text/image budgets), rejecting corrupt/encrypted files before model calls. File, filename, text and images stay in untrusted user-role data under the shared policy; fixed instructions remain cacheable. Validation diagnostics exclude rejected values. The shared mini profile and configured-provider selector do not allow caller-controlled models. Binary CV content is exempt from text scrubbing; processing requires a deployment-approved data arrangement.
Spreadsheet cells and attachment names Safe input text may still become a formula or unsafe header at output. src/common/security/tabular_exports.py and file_names.py; output behavior is described in the guide.
MCP protocol envelopes The SDK owns protocol parsing; agent arguments are checked separately. FastMCP validation and src/mcp_server/utils.py authentication. Pre-parse byte limits remain a server/proxy concern.
Health, metrics, documentation, numeric initialization IDs and lifecycle commands without free text There is no free-text input requiring a classifier. Existing typed IDs/authentication where applicable; Starlette StaticFiles provides documentation path containment.

Remaining risks and review boundaries

  • Frontend configuration identity: PUT /chatbot/api/v1/configs checks the supplied user_id for administrator status without binding it to the authenticated JWT subject. Resolving this requires a decision about existing API-key-only clients; text/image validation does not resolve authorization.
  • Request bytes: proxy/server ceilings before JSON or protocol parsing remain necessary. Post-parse budgets and SDK validation do not provide them.
  • Frontend rendering: HTML/Markdown escaping belongs to clients outside this Python repository; backend text checks preserve the supplied content.
  • LLM evaluation: mocked/adversarial role tests establish message placement and blocking order, not real classifier recall. No live-provider adversarial evaluation was performed.

For a changed input, update its group when it shares the same protection; add an endpoint/field-specific entry when its boundary, exemption or unresolved risk differs. Record the reason and source owner in English. No inventory status implies immunity to injection or a completed penetration test.

Live documentation catalogue

The documentation widget automatically reads public GenAI /catalogue, which has no request parameters and never loads tenant/application overrides. Its service reuses the deployment's provider readers and projects only model IDs, aliases, capabilities, shared profiles and independent code fallbacks. API keys, connection settings and raw provider documents are excluded. Shared text validation is inapplicable because the endpoint consumes no caller inputs; API key and Client-ID headers do not change its scope. Generation and tenant-aware /models retain their existing authentication.

The browser fetch stays on the instance origin, rejects redirects, omits credentials and disables caching. Database values are rendered through DOM textContent, never interpolated HTML. Refresh and page exit clear old results; failed requests never show stale data as current. Responses use no-store and correlation IDs; failures return a sanitized catalogue_unavailable error.

Static documentation lifecycle

src/core/documentation_runtime.py serves the public guides, HTTP contracts, search index and sitemap through Starlette StaticFiles containment checks. The static mount supports GET/HEAD and is excluded from OpenAPI. Documentation requires no authentication; protected business operations retain their own API-key dependencies. Shared text validation does not apply to static paths.

The build runs in an owned background subprocess. Until a complete site is published, requests receive a generic HTTP 503 page with Cache-Control: no-store. Build failures do not affect API availability or expose internal error details.

Lexi referential quality snapshots (NB-9113)

POST /lexi/api/v1/referentials/analyze requires Lexi API-key authentication and SafetyChecked snapshot data. Closed models reject unknown fields and broken entity graphs. HRX authenticates users and loads only the authorized organization. The analyzer performs no database writes. Versioned generation constrains repairs to selected job IDs, known groups and proficiency bounds. Snapshot text enters user context beneath the shared untrusted-context policy. CPU evaluation runs off the event loop; semantic enrichment reports failures and bounded-batch coverage explicitly. Owners: src/lexi/schemas/referential_analysis.py and src/lexi/services/referential_*.py.

Large scopes use a UUID-only QualityReference to pages held in private HRX storage. No caller URL is accepted. Every returned page passes the unchanged input-safety validator and a 256 KB transport bound before reassembly; only the total page count is unbounded by product policy. The source cannot override the generation configuration. Model prompts use relevant bounded context while the complete graph remains available to deterministic validation.