Work on RE engine for proof flags
This commit is contained in:
parent
a667f28984
commit
46cab38b8a
1 changed files with 230 additions and 88 deletions
|
|
@ -2,14 +2,16 @@ export type AIInferenceResult = {
|
||||||
aiGenerated: boolean;
|
aiGenerated: boolean;
|
||||||
aiProbability: number;
|
aiProbability: number;
|
||||||
humanProbability: number;
|
humanProbability: number;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const C2PASourceTypeScreenCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/screenCapture";
|
export const C2PASourceTypeScreenCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/screenCapture";
|
||||||
export const C2PASourceTypeDigitalCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture";
|
export const C2PASourceTypeDigitalCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/digitalCapture";
|
||||||
export const C2PASourceTypeComputationalCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture";
|
export const C2PASourceTypeComputationalCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/computationalCapture";
|
||||||
export const C2PASourceTypeCompositeCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/compositeCapture";
|
export const C2PASourceTypeCompositeCapture = "http://cv.iptc.org/newscodes/digitalsourcetype/compositeCapture";
|
||||||
export const C2PASourceTypeTrainedAlgorithmicMedia = "http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia";
|
export const C2PASourceTypeTrainedAlgorithmicMedia =
|
||||||
export const C2PASourceTypeCompositeWithTrainedAlgorithmicMedia = "http://cv.iptc.org/newscodes/digitalsourcetype/compositeWithTrainedAlgorithmicMedia";
|
"http://cv.iptc.org/newscodes/digitalsourcetype/trainedAlgorithmicMedia";
|
||||||
|
export const C2PASourceTypeCompositeWithTrainedAlgorithmicMedia =
|
||||||
|
"http://cv.iptc.org/newscodes/digitalsourcetype/compositeWithTrainedAlgorithmicMedia";
|
||||||
|
|
||||||
export type C2PAActionsAssertion = {
|
export type C2PAActionsAssertion = {
|
||||||
actions: {
|
actions: {
|
||||||
|
|
@ -17,37 +19,37 @@ export type C2PAActionsAssertion = {
|
||||||
softwareAgent?: string;
|
softwareAgent?: string;
|
||||||
digitalSourceType?: string;
|
digitalSourceType?: string;
|
||||||
}[];
|
}[];
|
||||||
}
|
};
|
||||||
|
|
||||||
export type C2PAAssertion = {
|
export type C2PAAssertion = {
|
||||||
label: string;
|
label: string;
|
||||||
data: C2PAActionsAssertion | undefined;
|
data: C2PAActionsAssertion | undefined;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type C2PAManifest = {
|
export type C2PAManifest = {
|
||||||
assertions: C2PAAssertion[];
|
assertions: C2PAAssertion[];
|
||||||
signature_info: {
|
signature_info: {
|
||||||
time: string;
|
time: string;
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export type C2PAValidationResults = {
|
export type C2PAValidationResults = {
|
||||||
activeManifest?: {
|
activeManifest?: {
|
||||||
failure: any[];
|
failure: any[];
|
||||||
success: any[];
|
success: any[];
|
||||||
informational: any[];
|
informational: any[];
|
||||||
}
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
export type C2PAManifestInfo = {
|
export type C2PAManifestInfo = {
|
||||||
active_manifest: string;
|
active_manifest: string;
|
||||||
manifests: { [key: string]: C2PAManifest };
|
manifests: { [key: string]: C2PAManifest };
|
||||||
validation_results?: C2PAValidationResults;
|
validation_results?: C2PAValidationResults;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type C2PAData = {
|
export type C2PAData = {
|
||||||
manifest_info: C2PAManifestInfo;
|
manifest_info: C2PAManifestInfo;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type Proof = {
|
export type Proof = {
|
||||||
data?: any;
|
data?: any;
|
||||||
|
|
@ -55,16 +57,153 @@ export type Proof = {
|
||||||
json?: string;
|
json?: string;
|
||||||
integrity?: { pgp?: any; c2pa?: C2PAData; exif?: { [key: string]: string | Object }; opentimestamps?: any };
|
integrity?: { pgp?: any; c2pa?: C2PAData; exif?: { [key: string]: string | Object }; opentimestamps?: any };
|
||||||
ai?: { inferenceResult?: AIInferenceResult };
|
ai?: { inferenceResult?: AIInferenceResult };
|
||||||
}
|
};
|
||||||
|
|
||||||
export type ProofHintFlags = {
|
export type ProofHintFlags = {
|
||||||
aiGenerated?: boolean;
|
aiGenerated?: boolean;
|
||||||
aiEdited?: boolean;
|
aiEdited?: boolean;
|
||||||
screenshot?: boolean;
|
screenshot?: boolean;
|
||||||
camera?: boolean;
|
camera?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export const extractProofHintFlags = (proof?: Proof): (ProofHintFlags | undefined) => {
|
type FlagMatchRule = {
|
||||||
|
field: string;
|
||||||
|
match: string[];
|
||||||
|
description: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FlagMatchInfo = {
|
||||||
|
field: string;
|
||||||
|
value: string;
|
||||||
|
re: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type FlagMatch = {
|
||||||
|
re: RegExp;
|
||||||
|
value: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ruleScreenshot = (): FlagMatchRule[] => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
field:
|
||||||
|
"integrity/c2pa/manifest_info/manifests[]/assertions[]/{label=c2pa.actions}/data/actions[]/{action=c2pa.created}/digitalSourceType",
|
||||||
|
match: [C2PASourceTypeScreenCapture],
|
||||||
|
description: "Screen capture",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const aiHintFlags = (): FlagMatchRule[] => {
|
||||||
|
const knownAIServices = [
|
||||||
|
"ChatGPT",
|
||||||
|
"OpenAI-API",
|
||||||
|
"Adobe Firefly",
|
||||||
|
"RunwayML",
|
||||||
|
"Runway AI",
|
||||||
|
"Google AI",
|
||||||
|
"Stable Diffusion",
|
||||||
|
];
|
||||||
|
return [
|
||||||
|
{ field: "name", match: ["^DALL_E_", "^Gen-3"], description: "File name" },
|
||||||
|
{
|
||||||
|
field: "integrity/c2pa/manifest_info/manifests[]/claim_generator",
|
||||||
|
match: knownAIServices,
|
||||||
|
description: "C2PA claim generator",
|
||||||
|
},
|
||||||
|
{ field: "iptc/Credit", match: knownAIServices, description: "IPTC Credit" },
|
||||||
|
{ field: "iptc/Provider", match: knownAIServices, description: "IPTC Provider" },
|
||||||
|
{ field: "iptc/ImageSupplier[]", match: knownAIServices, description: "IPTC ImageSupplier" },
|
||||||
|
{ field: "iptc/ImageCreator[]", match: knownAIServices, description: "IPTC ImageCreator" },
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
const matchFlag = (rules: FlagMatchRule[], file: any) => {
|
||||||
|
const matchParts = (parts: string[], o: any, re: RegExp[]): FlagMatch[] | undefined => {
|
||||||
|
if (parts.length == 0 || o == undefined) return undefined;
|
||||||
|
let part = parts[0];
|
||||||
|
if (part.endsWith("[]")) {
|
||||||
|
part = part.substring(0, part.length - 2);
|
||||||
|
if (o[part] != undefined) {
|
||||||
|
let opart: any[] = o[part];
|
||||||
|
if (!Array.isArray(opart)) {
|
||||||
|
opart = Object.values(opart) ?? [];
|
||||||
|
}
|
||||||
|
if (parts.length == 1) {
|
||||||
|
let strings = opart as string[];
|
||||||
|
if (!strings) return undefined;
|
||||||
|
return strings.reduce((res: FlagMatch[] | undefined, s: any) => {
|
||||||
|
return re.reduce((res: FlagMatch[] | undefined, r: any) => {
|
||||||
|
if (r.test(s)) {
|
||||||
|
const r2 = res || [];
|
||||||
|
r2.push({ re: r, value: s as string });
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}, res);
|
||||||
|
}, undefined);
|
||||||
|
} else {
|
||||||
|
return opart.reduce((res: FlagMatch[] | undefined, o: any) => {
|
||||||
|
let matches = matchParts(parts.slice(1), o, re);
|
||||||
|
if (matches) {
|
||||||
|
const r2 = res || [];
|
||||||
|
r2.push(...matches);
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}, undefined);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} else if (part.startsWith("{") && part.endsWith("}")) {
|
||||||
|
const [prop, val] = part.substring(1, part.length - 1).split("=");
|
||||||
|
if (o[prop] === val) {
|
||||||
|
return matchParts(parts.slice(1), o, re);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (o[part] != undefined) {
|
||||||
|
if (parts.length == 1) {
|
||||||
|
return re.reduce((res: FlagMatch[] | undefined, r: any) => {
|
||||||
|
if (r.test(o[part])) {
|
||||||
|
const r2 = res || [];
|
||||||
|
r2.push({ re: r, value: o[part] as string });
|
||||||
|
return r2;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}, undefined);
|
||||||
|
} else {
|
||||||
|
return matchParts(parts.slice(1), o[part], re);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = false;
|
||||||
|
let resultInfo: FlagMatchInfo[] = [];
|
||||||
|
for (let rule of rules) {
|
||||||
|
try {
|
||||||
|
const re: RegExp[] = (!Array.isArray(rule.match) ? [rule.match] : rule.match).map((m) => new RegExp(m, "gi"));
|
||||||
|
let parts = rule.field.split("/");
|
||||||
|
let matches = matchParts(parts, file, re);
|
||||||
|
if (matches) {
|
||||||
|
matches.forEach((m) => {
|
||||||
|
resultInfo.push({ field: rule.description, value: m.value, re: m.re.source });
|
||||||
|
});
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Invalid RE", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { result: result, matches: resultInfo };
|
||||||
|
};
|
||||||
|
|
||||||
|
export const extractProofHintFlags = (proof?: Proof): ProofHintFlags | undefined => {
|
||||||
if (!proof) return undefined;
|
if (!proof) return undefined;
|
||||||
|
|
||||||
let foundCreated = false;
|
let foundCreated = false;
|
||||||
|
|
@ -110,21 +249,24 @@ export const extractProofHintFlags = (proof?: Proof): (ProofHintFlags | undefine
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (foundCreated) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (foundCreated) {
|
if (foundCreated) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (valid) {
|
if (valid) {
|
||||||
|
screenshot = matchFlag(ruleScreenshot(), proof).result;
|
||||||
const flags: ProofHintFlags = {
|
const flags: ProofHintFlags = {
|
||||||
aiGenerated,
|
aiGenerated,
|
||||||
aiEdited,
|
aiEdited,
|
||||||
screenshot,
|
screenshot,
|
||||||
camera
|
camera,
|
||||||
};
|
};
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {}
|
||||||
}
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue