Enterprise knowledge base AI · SharePoint Copilot · Confluence AI
Prompt injection in SharePoint Copilot and Confluence AI — enterprise knowledge base poisoning via adversarial document images
Microsoft 365 Copilot (including Copilot for SharePoint and SharePoint Syntex AI) and Atlassian Intelligence for Confluence AI are the two dominant enterprise knowledge base AI platforms. Both let employees query shared internal documentation using natural language: “What is our vendor onboarding process?”, “Summarise the Q3 product roadmap”, “What does our HR policy say about remote work?” The AI reads and summarises content drawn from shared SharePoint libraries, Confluence spaces, and — in the case of M365 Copilot — every SharePoint site the querying user has access to. Notion AI presents the same architecture in a slightly different product surface. The attack surface that all of these tools share is deceptively simple: any employee, contractor, partner, or external vendor who can upload a document to a shared library or space can influence what the AI returns to every other user querying that corpus. A single adversarially crafted document image — a product specification PDF with a pixel-level instruction payload embedded in a chart, a compliance certificate with typographic injection content invisible at normal reading size, a vendor-submitted scanned contract with a steganographic payload — poisons AI knowledge base responses for the entire organisation, not just the individual who opened the file. This is corpus poisoning, and it is categorically more persistent than a one-time injection attack: the adversarial document remains in the corpus until it is specifically identified and removed, silently influencing every subsequent query that causes the AI to retrieve it. Unlike one-time injections (a malicious prompt in a single chat message), corpus poisoning attacks propagate forward through time and across users without any further action by the attacker. See also: Microsoft 365 Copilot prompt injection scanner for the full M365 attack surface.
TL;DR
SharePoint libraries and Confluence spaces are multiuser-write corpora fed directly into enterprise AI query pipelines. A pre-ingestion scan gate — scanning document images with POST https://glyphward.com/v1/scan before the document is indexed or made available to Copilot or Confluence AI — prevents adversarially crafted images from entering the knowledge base corpus. Reject documents with any image returning score >= 65 and route them to security review before indexing. For SharePoint, implement the scan gate as an Azure Functions trigger on the document library. For Confluence, add the scan step in the upload webhook or the CI/CD pipeline that publishes documentation. Free tier — 10 scans/day, no card required.
Four multimodal injection surfaces in enterprise knowledge base AI
1. Vendor-submitted onboarding documents in SharePoint libraries. Procurement and vendor management teams routinely ask suppliers and partners to submit scanned contracts, product documentation, compliance certificates, and onboarding packs to shared SharePoint libraries — often directly as part of a vendor portal or a Power Automate–driven onboarding workflow. These documents originate entirely from the external party, and they frequently contain images: company logos, scanned signature pages, product photographs, certification badges, and charts. When M365 Copilot or Copilot for SharePoint indexes this library, every image-bearing document in it becomes part of the AI’s retrieval corpus for that space. A vendor who embeds an adversarial pixel payload in a document image — a manipulated product specification chart, a scanned certificate with typographic injection content scaled below comfortable reading size — causes that payload to persist in the knowledge base and be retrieved whenever a procurement or legal employee asks Copilot about that vendor. The AI’s response to “What certifications does Vendor X hold?” or “What are the key terms in the Vendor X contract?” can be manipulated to return attacker-chosen information: suppressed liability clauses, false certification claims, or steered purchasing recommendations. The attack does not require the vendor to access any internal system — they only need to include a crafted image in a document they are invited to submit.
2. Confluence space contamination via customer support screenshots. Support engineering teams routinely document known product issues, reproduction steps, and bug investigations by pasting or uploading customer-submitted screenshots into Confluence pages. A customer experiencing an issue screenshots their screen and attaches it to a support ticket; the support engineer pastes that screenshot into a Confluence page in the engineering or product space to document the issue for the broader team. This is standard practice, and it means that customer-controlled image content regularly enters the Confluence knowledge base. When Confluence AI (Atlassian Intelligence) answers questions like “Is there a known issue with feature X?” or “What was the root cause of the Y incident?”, it draws on the Confluence pages that document those issues — including the customer-submitted screenshots embedded in them. An adversarial customer who submits a screenshot containing adversarially crafted pixel content as part of a support ticket causes that payload to be embedded in the Confluence knowledge base for the entire support and engineering team. The injected content can steer AI responses about product behaviour, incident history, or known issues for every engineer and support agent who queries that Confluence space — potentially influencing triage decisions, customer-facing communications, or escalation decisions.
3. Cross-space retrieval in Microsoft 365 Copilot. M365 Copilot’s most distinctive and most dangerous characteristic for corpus poisoning is that it does not respect SharePoint space boundaries when answering user queries: it searches across all SharePoint sites and OneDrive folders the querying user has access to, returning results from whichever sites contain the most relevant content regardless of which site the user is currently viewing. This cross-space retrieval design means that an adversarially crafted image uploaded to a low-sensitivity space — a general FAQ library, an all-staff announcements site, a public-facing intranet page — can influence Copilot responses when the user queries a higher-sensitivity space, if both spaces appear in the same retrieval result set. An employee querying Copilot for HR policy details may receive a response shaped in part by content retrieved from the general FAQ space where the adversarially crafted document resides. The user has no visibility into which source documents contributed to the Copilot answer, and the cross-space retrieval means the adversarial document does not need to be placed in the sensitive space itself — any space the target user can access suffices. This substantially broadens the adversarial opportunity compared to single-space knowledge base tools like Confluence.
4. SharePoint Syntex AI document processing with external-party uploads. SharePoint Syntex (now part of Microsoft 365 as SharePoint Premium AI) provides document understanding models that automatically extract structured field values from uploaded documents: invoice line items, contract parties and dates, form field values, and regulatory submission data. These extraction models process document images — rendered PDF pages, scanned form images — and return structured output that flows directly into downstream SharePoint lists, Power Automate workflows, and connected line-of-business systems. Syntex document processing workflows are frequently applied to external-party submissions: vendor invoices, supplier onboarding forms, regulatory filings, and insurance claims. An adversarial image embedded in a submitted form or invoice — a pixel-level payload in the form’s header image, a steganographic instruction in the company logo — can manipulate the field values extracted by the Syntex AI model. Manipulated extracted values entering downstream workflows can corrupt SharePoint list data, trigger incorrect Power Automate actions, or insert attacker-controlled strings into business process records — without any human review of the individual extracted fields before they enter the workflow.
Integration: SharePoint document upload pre-scan with Glyphward
import base64
import io
import logging
import zipfile
import fitz # PyMuPDF — pip install pymupdf
import requests
import azure.functions as func
from azure.identity import DefaultAzureCredential
from azure.storage.blob import BlobServiceClient
GLYPHWARD_KEY = "<your-glyphward-api-key>"
GLYPHWARD_THRESHOLD = 65
QUARANTINE_CONTAINER = "quarantine-pending-review"
logger = logging.getLogger(__name__)
def scan_image_bytes(image_bytes: bytes, source: str = "sharepoint_upload") -> dict:
"""Scan a single image for multimodal prompt injection."""
encoded = base64.b64encode(image_bytes).decode()
resp = requests.post(
"https://glyphward.com/v1/scan",
json={"image": encoded, "source": source},
headers={"Authorization": f"Bearer {GLYPHWARD_KEY}"},
timeout=8,
)
resp.raise_for_status()
return resp.json()
def extract_images_from_docx(doc_bytes: bytes) -> list[bytes]:
"""Extract embedded images from a Word .docx file (OOXML zip container)."""
images = []
with zipfile.ZipFile(io.BytesIO(doc_bytes)) as z:
for name in z.namelist():
if name.startswith("word/media/") and any(
name.lower().endswith(ext) for ext in [".png", ".jpg", ".jpeg", ".gif", ".tiff", ".bmp"]
):
images.append(z.read(name))
return images
def extract_images_from_pdf(pdf_bytes: bytes, dpi: int = 150) -> list[bytes]:
"""Render each PDF page as a PNG image for scanning."""
doc = fitz.open(stream=pdf_bytes, filetype="pdf")
images = []
for page in doc:
mat = fitz.Matrix(dpi / 72, dpi / 72)
pix = page.get_pixmap(matrix=mat, colorspace=fitz.csRGB)
images.append(pix.tobytes("png"))
return images
def scan_document_for_injection(
file_bytes: bytes,
filename: str,
) -> dict:
"""
SharePoint pre-ingestion scan: extract and scan all images in an uploaded document.
Supports .pdf, .docx, .pptx.
Returns {'status': 'clean'} or {'status': 'flagged', 'flagged_images': [...]}
"""
ext = filename.rsplit(".", 1)[-1].lower() if "." in filename else ""
if ext == "pdf":
images = extract_images_from_pdf(file_bytes)
image_type = "pdf_page"
elif ext == "docx":
images = extract_images_from_docx(file_bytes)
image_type = "docx_embedded"
elif ext == "pptx":
# PowerPoint: images stored in ppt/media/
images = []
with zipfile.ZipFile(io.BytesIO(file_bytes)) as z:
for name in z.namelist():
if name.startswith("ppt/media/") and any(
name.lower().endswith(ext2) for ext2 in [".png", ".jpg", ".jpeg", ".gif", ".tiff", ".bmp"]
):
images.append(z.read(name))
image_type = "pptx_slide"
else:
# Unsupported format — pass through (add additional parsers as needed)
return {"status": "clean", "images_scanned": 0, "note": "unsupported_format_skipped"}
flagged = []
for idx, img_bytes in enumerate(images):
try:
result = scan_image_bytes(img_bytes, source=f"sharepoint_{image_type}")
except Exception as exc:
logger.error("Glyphward scan failed for image %d in %s: %s", idx, filename, exc)
# Fail-closed: treat scanner unavailability as a block
return {
"status": "flagged",
"reason": f"scanner_unavailable: {exc}",
"flagged_images": [{"index": idx, "reason": "scan_failed"}],
}
if result["score"] >= GLYPHWARD_THRESHOLD:
flagged.append({
"index": idx,
"type": image_type,
"score": result["score"],
"scan_id": result["scan_id"],
})
if flagged:
return {"status": "flagged", "flagged_images": flagged, "total_images": len(images)}
return {"status": "clean", "images_scanned": len(images)}
# — Azure Functions entry point —
# Triggered by Microsoft Graph change notifications on a SharePoint document library.
# Configure the Graph webhook subscription to POST to this function on file creation/update.
app = func.FunctionApp()
@app.function_name("SharePointPreIngestScan")
@app.route(route="sharepoint-scan", methods=["POST"])
def sharepoint_pre_ingest_scan(req: func.HttpRequest) -> func.HttpResponse:
"""
Azure Function: receive Graph change notification, download file via Graph API,
scan with Glyphward, quarantine flagged files before SharePoint Copilot indexes them.
"""
body = req.get_json()
# Graph change notification validation handshake
if "validationToken" in req.params:
return func.HttpResponse(req.params["validationToken"], mimetype="text/plain")
credential = DefaultAzureCredential()
for change in body.get("value", []):
site_id = change.get("siteId")
drive_id = change.get("driveId")
item_id = change.get("id")
filename = change.get("name", "unknown")
if not all([site_id, drive_id, item_id]):
continue
# Download file bytes via Microsoft Graph
token = credential.get_token("https://graph.microsoft.com/.default").token
download_url = (
f"https://graph.microsoft.com/v1.0/sites/{site_id}"
f"/drives/{drive_id}/items/{item_id}/content"
)
file_resp = requests.get(
download_url,
headers={"Authorization": f"Bearer {token}"},
timeout=30,
)
if not file_resp.ok:
logger.warning("Failed to download %s: HTTP %d", filename, file_resp.status_code)
continue
scan_result = scan_document_for_injection(file_resp.content, filename)
if scan_result["status"] == "flagged":
logger.warning(
"FLAGGED document %s — %d image(s) exceeded threshold. Moving to quarantine.",
filename,
len(scan_result.get("flagged_images", [])),
)
# Move file to quarantine blob storage pending security review
blob_client = BlobServiceClient(
account_url="<your-storage-account-url>",
credential=credential,
)
blob_client.get_blob_client(
container=QUARANTINE_CONTAINER, blob=filename
).upload_blob(file_resp.content, overwrite=True)
# TODO: call Graph API to delete or restrict the item in SharePoint
# until security review completes, preventing Copilot indexing
else:
logger.info(
"CLEAN document %s — %d image(s) scanned, all passed.",
filename,
scan_result.get("images_scanned", 0),
)
return func.HttpResponse("OK", status_code=200)
This Azure Functions pattern uses Microsoft Graph change notifications (webhooks) to trigger a scan whenever a new document is created or updated in a monitored SharePoint library. The function downloads the file via Graph, extracts all embedded images (PDF page renders, OOXML-embedded images, PowerPoint slide media), and scans each with Glyphward before Copilot’s indexing job can process the document. Flagged documents are moved to a quarantine blob container and should be removed or permission-restricted in SharePoint — preventing M365 Copilot from indexing and retrieving them — until a security reviewer clears them. The function fails closed: if the scanner is unavailable, documents are quarantined rather than passed through. For high-volume libraries, configure the Azure Function with Premium plan concurrency to handle burst upload events without queue delays. Get early access
Coverage matrix
| Defence layer | Vendor document knowledge base poisoning | Customer screenshot Confluence poisoning | M365 Copilot cross-space retrieval | SharePoint Syntex form injection |
|---|---|---|---|---|
| SharePoint sensitivity labels / DLP | No — classifies data by sensitivity label; does not inspect pixel content of images in labelled documents | N/A — Confluence uses separate DLP controls | No — DLP controls data sharing, not retrieval content integrity | No — Syntex extraction output is not DLP-scanned before entering workflows |
| Azure Prompt Shields (M365 Copilot text path) | Partial — screens text-layer content for prompt injection; does not inspect image pixel content in documents | N/A | Partial — text path only; cross-space image content bypasses Prompt Shields | No — Syntex extraction pipeline does not route through Prompt Shields |
| Defender for Office 365 attachment scanning | No — detonates attachments for malware signatures; adversarial pixel payloads in valid image files are not malware and are not detected | No | No | No |
| Glyphward pre-ingestion multimodal scan | Yes — scan extracted images before SharePoint document is indexed by Copilot | Yes — scan screenshots before Confluence page is published and indexed by Atlassian Intelligence | Yes — pre-ingestion scan prevents adversarial images entering the cross-space retrieval corpus | Yes — scan form/invoice images before Syntex AI extraction processes them into downstream workflows |
Related questions
Does Microsoft 365 Copilot already protect against prompt injection in SharePoint content?
Microsoft’s current Copilot security stack for SharePoint content consists of Azure Prompt Shields (which screens text-layer content for adversarial instructions), Microsoft Purview DLP (which controls data sensitivity labels and information barriers), and Defender for Office 365 (which scans file attachments for malware). None of these controls operate on the pixel content of images embedded in documents stored in SharePoint libraries. Azure Prompt Shields processes the text that M365 Copilot returns and, in some configurations, the text content extracted from documents — but the multimodal path, where the VLM processes an image embedded in a document, does not route through Prompt Shields’ text-injection detection. Microsoft’s own prompt injection mitigation guidance focuses on the text path. The pixel-level injection surface in SharePoint document images is currently unaddressed by first-party Microsoft controls. Glyphward provides the missing pre-ingestion image scan gate.
How persistent is a knowledge base poisoning attack compared to a one-time injection?
A one-time injection attack — an adversarial prompt in a single user’s chat message — affects a single session. Once that conversation is over, the injection has no further effect. A corpus poisoning attack via an adversarially crafted knowledge base document is categorically more severe: the adversarial document remains in the SharePoint library or Confluence space until it is explicitly identified and removed. Every query to the AI that causes the adversarial document to be retrieved — which may be many queries per day, from many employees, for months or years — is influenced by the injected payload. The attacker does not need to maintain any ongoing access or repeat any action after the initial document upload. The poisoning is also invisible to users: M365 Copilot and Confluence AI do not indicate which specific source documents shaped a given response, so employees have no way to identify that a response was influenced by an adversarial document without forensic investigation. This combination of persistence, breadth, and invisibility makes corpus poisoning the highest-severity variant of the prompt injection threat model for enterprise knowledge base AI.
Which Confluence AI features are most exposed to adversarial document images?
Atlassian Intelligence (Confluence AI) features that are most exposed to adversarially crafted images are those that summarise or answer questions about Confluence page content: the “Summarise this page” feature, the Confluence AI search (“Find information about X across spaces”), Rovo (Atlassian’s AI assistant that answers questions drawing on Confluence content), and any Atlassian Intelligence feature that accesses Confluence space content as a retrieval corpus. Features that operate only on structured data (issue fields in Jira, table data) are less exposed than features that process free-form page content including embedded images. The most dangerous surface is Rovo’s cross-space search and summarisation — analogous to M365 Copilot’s cross-site retrieval — because a single adversarial image in any Confluence space Rovo has access to can influence answers about content across the entire Atlassian workspace. Teams using Confluence AI should apply pre-upload image scanning to all external content (customer screenshots, vendor documents, partner submissions) before those documents are published to Confluence spaces indexed by Atlassian Intelligence.
How does this relate to OWASP LLM03 training data poisoning?
OWASP LLM03 (Training Data Poisoning) describes attacks that corrupt the training data used to fine-tune or train a model, causing the model itself to behave incorrectly. SharePoint and Confluence corpus poisoning is distinct: it is a retrieval corpus poisoning attack, not a training data attack. The model’s weights are not modified; instead, the documents that the RAG (retrieval-augmented generation) pipeline retrieves and includes in the model’s context window at query time are corrupted. The practical impact is similar — the AI returns attacker-influenced outputs — but the mechanism and the remediation differ. Training data poisoning requires retraining or fine-tuning to remediate; retrieval corpus poisoning is remediated by removing the adversarial document from the knowledge base. OWASP LLM01 (Prompt Injection) is the more direct classification for this attack, specifically the indirect prompt injection variant where the injected instruction arrives via a retrieved document rather than directly from the user. The LLM03 page covers the fine-tuning dataset poisoning surface; this page covers the live retrieval corpus poisoning surface that affects production RAG deployments like SharePoint Copilot and Confluence AI.
Further reading
- Prompt injection scanner for Microsoft 365 Copilot — full M365 Copilot attack surface including Word, PowerPoint, Teams, and SharePoint document processing
- Prompt injection scanner for Microsoft Copilot Studio — custom Copilot agents built on the Copilot Studio platform (distinct from M365 Copilot knowledge base AI)
- OWASP LLM03 training data poisoning — multimodal dimension — the fine-tuning dataset poisoning surface that complements retrieval corpus poisoning
- PDF prompt injection detection — PDF page image extraction and scanning patterns for SharePoint library uploads
- Glyphward API free tier — 10 scans/day at no cost, no credit card required