Understanding FPSL with example patterns

Following are some examples of FPSL patterns that Forcepoint ONE SSE have created that you can either use in your own tenants or that you can use a template for creating your own.

FPSL is a powerful tool with limitless capabilities. As such, customers can use this to create any policy they might need to control inline actions and activity across any cloud service. Forcepoint ONE SSE's eventual goal will be to publish the data patterns created and tested as out of the box policies that customers can use. Then moving forward as customers create their own (since the majority of Lua code creations will not have customer info) we will take common use case ones, test them, and then add them to our out of the box pattern library. In the meantime, below are some examples of FPSL patterns that we have created that you can either use in your own tenants or that you can use a template for creating your own.

Important: The following LUA code samples serve as examples only. As websites update their URLs, you may need to modify the code accordingly. Always test and adjust the LUA code as necessary before implementing it in your tenant.

LinkedIn Message

This pattern will identify when someone tries to post a message on LinkedIn.

[[LUA_AF_REQUEST_SCOPE]]
if ((BG.method == "POST")
and BG.domain:find("www%.linkedin%.com")
and BG.uri:find("^/voyager/api/messaging/conversations/")
and BG.qs:find("create")) then
BGResult.match = 1
BGResult.log = "LinkedIn Messaging"
end

LinkedIn Posts

This pattern will identify when someone tries to make a post on LinkedIn.

[[LUA_AF_REQUEST_SCOPE]]
if BG.uri then
local qs = BG.qs
if not qs then qs = "" end
local uri = string.lower(BG.uri)
if (((BG.method == "POST") or (BG.method == "GET")) and
        (string.find(uri, "/api/contentcreation/") or
        (uri == "/") or
        string.find(qs, "commentaryV%d+"))) then
BGResult.match = 1
BGResult.log = "LinkedIn Posting"
        end
end

LinkedIn Comments

This pattern will identify when someone tries to reply to a LinkedIn post in the comments.

[[LUA_AF_REQUEST_SCOPE]]
if BG.uri then
local qs = BG.qs
if not qs then qs = "" end
local uri = string.lower(BG.uri)
if (((BG.method == "POST") or (BG.method == "GET")) and
        (string.find(uri, "/api/feed/comments") or
        (uri == "/") or
        string.find(qs, "comment"))) then
BGResult.match = 1
BGResult.log = "LinkedIn Comment"
        end
end

LinkedIn Reactions

This pattern is separate to identify reactions such as Likes, Celebrates, Support, etc on LinkedIn

[[LUA_AF_REQUEST_SCOPE]]
if BG.uri then
local qs = BG.qs
if not qs then qs = "" end
local uri = string.lower(BG.uri)
if (((BG.method == "POST") or (BG.method == "GET")) and
        (string.find(uri, "/api/feed/reactions") or
        (uri == "/") or
        string.find(qs, "react-to-feed-post"))) then
BGResult.match = 1
BGResult.log = "LinkedIn Reactions"
        end
end

Slack Login Control via Email and Password

This pattern will distinguish between personal and corporate slack logins to block personal accounts. For patterns like this, you will need to enter your own email domain to help distinguish when a match is found (that is, in the below you are calling it a match when it does not see your email domain so you match on personal accounts to block them).

Note: When logging into slack, regardless if you are going through the web or from the app, you will be directed to login in a web browser. There are two options for logging in, this one covers when you enter your email and password directly.

For Forcepoint ONE SSE <YourEmailDomain>, below would look like bitglass%.com$

[[LUA_AF_REQUEST_SCOPE]]
local function NonBitglassLogin(keyValueTable)
        if not keyValueTable["email"] then
        return false
end
if string.find(keyValueTable["email"], "<YourEmailDomain>")
then
return false
end
return true
end
if ((BG.method == "POST") and
        string.find(BG.domain, "slack%.com$") and
        BG.MatchFormFields(NonBitglassLogin))
        then
BGResult.match = 1
BGResult.log = "Non-Bitglass Login"
end

Slack Email Login Control via Email Confirmation

This pattern will help you block personal logins when users click the email confirmation option for logging in which sends the user a 2FA code to the email they enter instead of having them login via their configured password (see above).

[[LUA_AF_REQUEST_SCOPE]]
if ((BG.method == "POST") and
        BG.domain:find("slack%.com$") and
        BG.uri:find("/api/signup.checkEmail")) then
BGResult.match = 1
end

Slack Message and Attachment Control

This policy will control if users can post messages or attachments to slack (does not matter if it's a channel or direct message).

[[LUA_AF_REQUEST_SCOPE]]
if ((BG.method == "POST") and
        BG.domain:find("%.slack%.com$") and
        (BG.uri:find("/api/chat.postMessage") or BG.uri:find("/upload")))
then
BGResult.match = 1
BGResult.log = "Block Channel"
end

If you wish to only control messages or attachments, you can remove the corresponding URI field above.

  • Message Posting: /api/chat.postMessage
  • Attachment Upload: /upload

Slack Combined Channel Control

Slack assigns every channel a unique identifier. Customers can create advanced patterns to identify and block posts and/or attachments to those channels or conversely restrict access to only specific channels. This action will require 3 data patterns.

  • First you will need to create a pattern with the message and attachment control above.
  • Then create a simple pattern with the keyword set to the identifier of the channel. You can easily find this identifier by accessing slack in a web browser and looking at the end of the URL when you are clicked into a channel (for example, this for one of our channels you use)





  • Finally, you will need a combined pattern adding the two

    Count("<Name of Slack Channel ID Pattern>") and Count("<Name of Slack Post/Message Control Pattern>")

Facebook Likes

This pattern will allow you to control Facebook likes.

[[LUA_AF_REQUEST_SCOPE]]
local function FBLike(keyValueTable)
        if keyValueTable["fb_api_req_friendly_name"] == "CometUFIFeedbackReactMutation" then
local v = keyValueTable["variables"]
        if v and string.find(v, "feedback_reaction") then
return true
end
end
return false
end
if ((BG.method == "POST") and
        string.find(BG.domain, "facebook%.com") and
        string.find(BG.uri, "/api/graphql") and
        BG.MatchFormFields(FBLike)) then
BGResult.match = 1
BGResult.log = "Facebook like"
end

AWS Personal Console Control

This pattern will identify if users are trying to access personal AWS consoles.

[[LUA_AF_REQUEST_SCOPE]]
function IsUnsanctionedUser()
if not BG.cookie then
return false
end
if string.find(string.lower(BG.cookie), "<Your Company Name") then
return false
end
return true
end
if ((BG.method == "POST") and
        string.find(BG.domain, "console%.aws%.amazon%.com$") and
        string.find(string.lower(BG.uri), "/batchopsservlet%-proxy") and
        IsUnsanctionedUser()) then
BGResult.match = 1
BGResult.log = "Amazon S3 upload by unsanctioned user."
end

For <Your Company Name>, you do not need the full domain since you are trying to identify the specific AWS subdomain. For example for Forcepoint ONE SSE, you would simply enter bitglass for <Your Company Name>.

Microsoft 365 Identify Personal Login

This pattern will identify personal login attempts to M365 for admins to block or control. You will need to enter your domain where stated in order for the policy to match when your domain is not seen during a user's login attempt.

[[LUA_AF_REQUEST_SCOPE]]
local corp = "<Your Domain>"
local function NonCorpLogin(keyValueTable)
local un = keyValueTable["username"]
if not un then
return false
end
if (string.find(un, "@" .. corp .. "$") or
string.find(un, "40" .. corp .. "$")) then
return false
end
return true
end
if ((BG.method == "POST")
        and BG.domain:find("^login%.microsoftonline%.com$")
        and BG.uri:find("^/common/GetCredentialType")
        and BG.MatchFormFields(NonCorpLogin)) then
BGResult.match = 1
BGResult.log = "Non-Corporate Login"
end

Microsoft Prevent Sharing Controls

This pattern will help identify the action of sharing a file with an external user.

[[LUA_AF_REQUEST_SCOPE]]
if ((BG.method == "POST")
and BG.domain:find("%.sharepoint%.com$")
and BG.uri:find("/ShareLink")) then
result = ""
owner = BG.uri:match(".*/(%S+)/_api/")
if (owner) then
owner = owner:gsub("_", "@", 1)
if (owner) then
owner = owner:gsub("_", ".")
end
end
loc = BG.domain:find("%.sharepoint%.com")
if (loc) then
sp_or_od = BG.domain:sub(1, loc-1)
if (sp_or_od:find("%-my$")) then
result = "OneDrive link shared by: " .. owner .. ", "
else
result = result .. "SharePoint link shared: "
end
result = result .. BG.domain .. BG.uri .. "?" .. BG.qs
end
BGResult.match = 1
BGResult.log = result
end